{"version":"https://jsonfeed.org/version/1.1","title":"Matias Kinnunen – Blog","home_page_url":"https://mtsknn.fi/blog/","feed_url":"https://mtsknn.fi/feeds/blog.json","description":"A collection of my wildest adventures with computers. Mostly about web development.","favicon":"https://mtsknn.fi/apple-touch-icon.png","authors":[{"name":"Matias Kinnunen","url":"https://mtsknn.fi/"}],"language":"en-US","items":[{"id":"https://mtsknn.fi/blog/indenting-ordered-lists-in-markdown/","url":"https://mtsknn.fi/blog/indenting-ordered-lists-in-markdown/","title":"Better way to indent ordered lists in Markdown","content_html":"<p><code>1. foo</code> is annoying because the list item's text is indented with three spaces.\nSolution: add another space after the period.</p>\n<p>Before:</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token list punctuation\">1.</span> Using <span class=\"token bold\"><span class=\"token punctuation\">**</span><span class=\"token content\">3</span><span class=\"token punctuation\">**</span></span> spaces of indentation.\n<span class=\"token list punctuation\">2.</span> ...\n<span class=\"token list punctuation\">3.</span> Code blocks are <span class=\"token bold\"><span class=\"token punctuation\">**</span><span class=\"token content\">especially annoying</span><span class=\"token punctuation\">**</span></span>:\n   ```js\n   // <span class=\"token bold\"><span class=\"token punctuation\">**</span><span class=\"token content\">3</span><span class=\"token punctuation\">**</span></span> spaces of indentation, yuck\n   function foo() {\n     // <span class=\"token bold\"><span class=\"token punctuation\">**</span><span class=\"token content\">5</span><span class=\"token punctuation\">**</span></span> spaces of indentation, yuck\n     alert('lolz')\n   }\n   ```\n<span class=\"token list punctuation\">4.</span> ...\n<span class=\"token list punctuation\">5.</span> And when there are lots of list items:\n<span class=\"token list punctuation\">6.</span> ...\n<span class=\"token list punctuation\">7.</span> ...\n<span class=\"token list punctuation\">8.</span> ...\n<span class=\"token list punctuation\">9.</span> ...\n<span class=\"token list punctuation\">10.</span> ...the indentation <span class=\"token bold\"><span class=\"token punctuation\">**</span><span class=\"token content\">changes to 4 spaces</span><span class=\"token punctuation\">**</span></span> (nice but inconsistent)\n</span></code></pre>\n<p>After:</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token list punctuation\">1.</span>  Using <span class=\"token bold\"><span class=\"token punctuation\">**</span><span class=\"token content\">4</span><span class=\"token punctuation\">**</span></span> spaces of indentation.\n<span class=\"token list punctuation\">2.</span>  ...\n<span class=\"token list punctuation\">3.</span>  Code blocks are <span class=\"token bold\"><span class=\"token punctuation\">**</span><span class=\"token content\">now ok</span><span class=\"token punctuation\">**</span></span>:\n    ```js\n    // <span class=\"token bold\"><span class=\"token punctuation\">**</span><span class=\"token content\">4</span><span class=\"token punctuation\">**</span></span> spaces of indentation, sweet\n    function foo() {\n      // <span class=\"token bold\"><span class=\"token punctuation\">**</span><span class=\"token content\">6</span><span class=\"token punctuation\">**</span></span> spaces of indentation, sweet\n      alert('lolz')\n    }\n    ```\n<span class=\"token list punctuation\">4.</span>  ...\n<span class=\"token list punctuation\">5.</span>  And when there are lots of list items:\n<span class=\"token list punctuation\">6.</span>  ...\n<span class=\"token list punctuation\">7.</span>  ...\n<span class=\"token list punctuation\">8.</span>  ...\n<span class=\"token list punctuation\">9.</span>  ...\n<span class=\"token list punctuation\">10.</span> ...the indentation <span class=\"token bold\"><span class=\"token punctuation\">**</span><span class=\"token content\">is still consistently 4 spaces</span><span class=\"token punctuation\">**</span></span>, yay!\n</span></code></pre>\n<p>This kind of indentation is also <a href=\"https://prettier.io/\" class=\"link link-external\">Prettier</a>-compatible, nice!\nMaybe it should even be the default indentation style.</p>\n<hr>\n<p>I noticed this accidentally when writing an answer on Stack Overflow,\nwhere the textarea formats ordered lists like this:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">Notice the leading spaces:\n\n <span class=\"token list punctuation\">1.</span> foo\n <span class=\"token list punctuation\">2.</span> bar\n</span></code></pre>\n<p>I copy-pasted that to VS Code,\nthen formatted the file in VS Code (using Prettier),\nand Prettier formatted the list like this:</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token list punctuation\">1.</span>  foo\n<span class=\"token list punctuation\">2.</span>  bar\n</span></code></pre>\n<p>A-ha!</p>\n","summary":"`1. foo` is annoying because the list item's text is indented with three spaces.\nSolution: add another space after the period.","date_published":"2024-10-15T12:00:00+03:00","tags":["Markdown"]},{"id":"https://mtsknn.fi/blog/github-html-preview-bookmarklet/","url":"https://mtsknn.fi/blog/github-html-preview-bookmarklet/","title":"Previewing HTML files directly on GitHub with a bookmarklet","content_html":"<p>There's no built-in way to preview HTML files on GitHub,\nbut you can use this nifty bookmarklet.\n(GitHub's project managers HATE it!)</p>\n<ol>\n<li>\n<p>Create a bookmark with this &quot;URL&quot;:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token literal-property property\">javascript</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> selector <span class=\"token operator\">=</span> <span class=\"token string\">'#read-only-cursor-text-area'</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> html <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">querySelector</span><span class=\"token punctuation\">(</span>selector<span class=\"token punctuation\">)</span><span class=\"token operator\">?.</span>value<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>html<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">alert</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Error: element </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>selector<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> not found</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  document<span class=\"token punctuation\">.</span><span class=\"token function\">open</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  document<span class=\"token punctuation\">.</span><span class=\"token function\">write</span><span class=\"token punctuation\">(</span>html<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  document<span class=\"token punctuation\">.</span><span class=\"token function\">close</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n</li>\n<li>\n<p>Open an HTML file on GitHub.</p>\n</li>\n<li>\n<p>Run the bookmarklet by &quot;opening&quot; the bookmark.\nIt replaces the current page's contents with the HTML file's contents.</p>\n</li>\n</ol>\n<p>Works great with self-contained / zero-dependency HTML files generated with <a href=\"https://github.com/gildas-lormeau/SingleFile\" class=\"link link-external\">SingleFile</a>.</p>\n<p>Notes:</p>\n<ul>\n<li>The bookmarklet breaks GitHub's client-side routing,\nso navigating back with the browser's Back button doesn't work.\nReloading the page (<kbd>F5</kbd>) fixes the issue.</li>\n<li>The bookmarklet doesn't work on raw.githubusercontent.com\nbecause of a strict <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP\" class=\"link link-external\">Content Security Policy (CSP)</a>.\n(Plus the bookmarklet is looking for an element that's not present on this domain.)</li>\n<li>The CSP used on github.com can also be too strict;\nin such cases, I would personally try an <a href=\"https://stackoverflow.com/a/37912931/1079869\" class=\"link link-external\">alternative solution on Stack Overflow by ostrokach</a>.</li>\n<li>The bookmarklet is brittle,\ni.e. it might need tweaking from time to time as GitHub gets updated.</li>\n</ul>\n<hr>\n<p>Alternative bookmarklet code that retains the page's header and footer,\nso that running the bookmarklet doesn't break client-side routing:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token literal-property property\">javascript</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> selector <span class=\"token operator\">=</span> <span class=\"token string\">'#read-only-cursor-text-area'</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> html <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">querySelector</span><span class=\"token punctuation\">(</span>selector<span class=\"token punctuation\">)</span><span class=\"token operator\">?.</span>value<span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>html<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">alert</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Error: element </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>selector<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> not found</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">const</span> $iframe <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'iframe'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  $iframe<span class=\"token punctuation\">.</span>style<span class=\"token punctuation\">.</span>height <span class=\"token operator\">=</span> <span class=\"token string\">'100vh'</span><span class=\"token punctuation\">;</span>\n  $iframe<span class=\"token punctuation\">.</span>style<span class=\"token punctuation\">.</span>width <span class=\"token operator\">=</span> <span class=\"token string\">'100%'</span><span class=\"token punctuation\">;</span>\n  $iframe<span class=\"token punctuation\">.</span>srcdoc <span class=\"token operator\">=</span> html<span class=\"token punctuation\">;</span>\n\n  document<span class=\"token punctuation\">.</span><span class=\"token function\">querySelector</span><span class=\"token punctuation\">(</span><span class=\"token string\">'turbo-frame'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">replaceChildren</span><span class=\"token punctuation\">(</span>$iframe<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  $iframe<span class=\"token punctuation\">.</span><span class=\"token function\">scrollIntoView</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n<p>The CSP on github.com blocks the iframe from loading most links,\nso with this version most links are effectively disabled\n(i.e. clicking a link doesn't do anything, at least on Firefox).\nIn my case this is a feature, not a bug.</p>\n<hr>\n<p>There are (at least) two relevant questions on Stack Overflow:</p>\n<ul>\n<li><a href=\"https://stackoverflow.com/q/8446218/1079869\" class=\"link link-external\"><em>How to see an HTML page on GitHub as a normal rendered HTML page to see preview in browser, without downloading?</em></a></li>\n<li><a href=\"https://stackoverflow.com/q/6551446/1079869\" class=\"link link-external\"><em>Can I run HTML files directly from GitHub, instead of just viewing their source?</em></a></li>\n</ul>\n<p>Neither question has as nice solutions as this one by me (until now).\nBookmarklets are underrated!</p>\n","summary":"There's no built-in way to preview HTML files on GitHub,\nbut you can use this nifty bookmarklet.\n(GitHub's project managers HATE it!)","date_published":"2024-10-14T12:00:00+03:00","tags":["Bookmarklets","JavaScript"]},{"id":"https://mtsknn.fi/blog/beware-implicit-values/","url":"https://mtsknn.fi/blog/beware-implicit-values/","title":"Beware implicit `Content-Type: \"application/json\"`","content_html":"<p>If you are sending a <code>FormData</code> body with <code>fetch</code>,\nthings might not work correctly if you are (accidentally)\nsetting the <code>Content-Type</code> request header to <code>&quot;application/json&quot;</code>.</p>\n<h2 id=\"problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problem\">Problem</a></h2>\n<p>I was sending a <code>FormData</code> object to a 3rd party API:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> formData <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FormData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nformData<span class=\"token punctuation\">.</span><span class=\"token function\">append</span><span class=\"token punctuation\">(</span><span class=\"token string\">'foo'</span><span class=\"token punctuation\">,</span> someValue<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">request</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https://example.com/api/'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  body<span class=\"token operator\">:</span> formData<span class=\"token punctuation\">,</span>\n  method<span class=\"token operator\">:</span> <span class=\"token string\">'POST'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>The API responses were confusing/incorrect.</p>\n<h2 id=\"culprit\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#culprit\">Culprit</a></h2>\n<p>I was using <code>request</code>,\nwhich is a thin wrapper around <code>fetch</code>.\nSomething like this:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">request</span><span class=\"token punctuation\">(</span>url<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span> options<span class=\"token operator\">?</span><span class=\"token operator\">:</span> RequestInit<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">...</span>options<span class=\"token punctuation\">,</span>\n    headers<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      Accept<span class=\"token operator\">:</span> <span class=\"token string\">'application/json'</span><span class=\"token punctuation\">,</span>\n      apiKey<span class=\"token operator\">:</span> <span class=\"token string\">'abc123'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\">:</span> <span class=\"token string\">'application/json'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token operator\">...</span>options<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">.</span>ok<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> response<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">// Handle errors etc.</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Notice how I'm defaulting the <code>Content-Type</code> header to <code>&quot;application/json&quot;</code>.</p>\n<p>This is handy because most of the time I'm sending JSON.</p>\n<p>But now I was sending a <code>FormData</code> object,\nso the implicit <code>&quot;application/json&quot;</code> value caused problems.</p>\n<p>It took me a while to realize\nthat this was the culprit for the confusing API responses.</p>\n<h2 id=\"fix-attempt-1\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#fix-attempt-1\">Fix attempt 1</a></h2>\n<p>First I tried to &quot;clear&quot; the default <code>Content-Type</code> value:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">request</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https://example.com/api/'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  body<span class=\"token operator\">:</span> formData<span class=\"token punctuation\">,</span>\n  headers<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\">:</span> <span class=\"token keyword\">undefined</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  method<span class=\"token operator\">:</span> <span class=\"token string\">'POST'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>But whoops,\nit caused a TypeScript error (header values must be strings),\nand the value was set to the string <code>&quot;undefined&quot;</code>.\nNot good.</p>\n<h2 id=\"fix-attempt-2\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#fix-attempt-2\">Fix attempt 2</a></h2>\n<p>Then I tried to set the value to <code>&quot;multipart/form-data&quot;</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">request</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https://example.com/api/'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  body<span class=\"token operator\">:</span> formData<span class=\"token punctuation\">,</span>\n  headers<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\">:</span> <span class=\"token string\">'multipart/form-data'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  method<span class=\"token operator\">:</span> <span class=\"token string\">'POST'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>But this didn't work either.</p>\n<h2 id=\"explanation\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#explanation\">Explanation</a></h2>\n<p>This snippet makes it clear why the 2nd fix attempt didn't work:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> formData <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FormData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> req <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Request</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https://example.com/api/'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  body<span class=\"token operator\">:</span> formData<span class=\"token punctuation\">,</span>\n  method<span class=\"token operator\">:</span> <span class=\"token string\">'POST'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token builtin\">console</span><span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span>req<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['content-type', 'multipart/form-data; boundary=---------------------------26864524337159351664014536661']</span>\n</span></code></pre>\n<p>So my <code>Content-Type</code> header value (<code>&quot;multipart/form-data&quot;</code>)\nwas missing a <code>boundary</code> value,\nwhich is automatically generated by the browser (or Node.js or whatever)\nand actually required.</p>\n<p>From <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type\" class=\"link link-external\"><code>Content-Type</code> on MDN</a>:</p>\n<blockquote>\n<p>For multipart entities, the <code>boundary</code> parameter is required.\nIt is used to demarcate the boundaries of the multiple parts of the message.\n[...]</p>\n</blockquote>\n<blockquote>\n<p>[A <code>multipart/form-data</code>] request looks something like the following example with some headers omitted for brevity.\nIn the request, a boundary of <code>ExampleBoundaryString</code> is used for illustration,\nbut in practice, a browser would create a string more like this <code>---------------------------1003363413119651595289485765</code>.</p>\n<pre aria-label=\"Code block\" class=\"language-http bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token request-line\"><span class=\"token method property\">POST</span> <span class=\"token request-target url\">/foo</span> <span class=\"token http-version property\">HTTP/1.1</span></span>\n<span class=\"token header\"><span class=\"token header-name keyword\">Content-Length</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">68137</span></span>\n<span class=\"token header\"><span class=\"token header-name keyword\">Content-Type</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">multipart/form-data; boundary=ExampleBoundaryString</span></span>\n\n--ExampleBoundaryString\n<span class=\"token header\"><span class=\"token header-name keyword\">Content-Disposition</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">form-data; name=\"description\"</span></span>\n\nDescription input value\n--ExampleBoundaryString\n<span class=\"token header\"><span class=\"token header-name keyword\">Content-Disposition</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">form-data; name=\"myFile\"; filename=\"foo.txt\"</span></span>\n<span class=\"token header\"><span class=\"token header-name keyword\">Content-Type</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">text/plain</span></span>\n<span class=\"token text-plain\">\n[content of the file foo.txt chosen by the user]\n--ExampleBoundaryString--\n</span></span></code></pre>\n</blockquote>\n<h2 id=\"final-fix\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#final-fix\">Final fix</a></h2>\n<p>The implicit <code>Content-Type: &quot;application/json&quot;</code> was the root cause,\nso I got rid of it.\nWell, at least partially:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">request</span><span class=\"token punctuation\">(</span>url<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">,</span> options<span class=\"token operator\">?</span><span class=\"token operator\">:</span> RequestInit<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">...</span>options<span class=\"token punctuation\">,</span>\n    headers<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      Accept<span class=\"token operator\">:</span> <span class=\"token string\">'application/json'</span><span class=\"token punctuation\">,</span>\n      apiKey<span class=\"token operator\">:</span> <span class=\"token string\">'abc123'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token operator\">...</span><span class=\"token punctuation\">(</span><span class=\"token operator\">!</span><span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">.</span>body <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\">FormData</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\">:</span> <span class=\"token string\">'application/json'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token operator\">...</span>options<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Now if the <code>body</code> is a <code>FormData</code> object,\n<code>fetch</code> automatically sets the <code>Content-Type</code> header to <code>multipart/form-data</code>\nwith an automatically generated <code>boundary</code> value.</p>\n<h2 id=\"lessons\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#lessons\">Lessons</a></h2>\n<p>My first thought was that while this hand-rolled wrapper around <code>fetch</code> has been handy,\nmaybe it would have been better to use a battle-tested library from the beginning.\nFor example,\n<a href=\"https://github.com/sindresorhus/ky\" class=\"link link-external\">Ky (&quot;a tiny and elegant HTTP client based on the Fetch API&quot;)</a>\nseems nice.</p>\n<p>Or, at least, one should be careful about default/implicit values.</p>\n<p>I wonder if the 3rd party API server should also be validating the request headers.\nThe API endpoint expects a <code>FormData</code> body,\nso maybe it should also check that the <code>Content-Type</code> request header is valid.\nIt would be a small thing to do,\nbut it would have saved me a great amount of time.</p>\n","summary":"If you are sending a `FormData` body with `fetch`,\nthings might not work correctly if you are (accidentally)\nsetting the `Content-Type` request header to `\"application/json\"`.","date_published":"2024-08-31T12:00:00+03:00","tags":["JavaScript","Node.js","TypeScript"]},{"id":"https://mtsknn.fi/blog/relative-tr-in-safari/","url":"https://mtsknn.fi/blog/relative-tr-in-safari/","title":"Safari doesn't support `position: relative` on `<tr>` elements","content_html":"<p>This is a problem if you want whole table rows to be clickable.\nLuckily there's a (hacky) workaround.</p>\n<h2 id=\"problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problem\">Problem</a></h2>\n<p>I want whole table rows (<code>&lt;tr&gt;</code>s) to be clickable.</p>\n<p>I don't want to:</p>\n<ul>\n<li>Wrap the <code>&lt;tr&gt;</code>s in <code>&lt;a&gt;</code> elements either\nbecause that wouldn't be semantic.\n(Not sure if this would even work; haven't tested.)</li>\n<li>Use <code>onClick</code> on the <code>&lt;tr&gt;</code> elements\nbecause that wouldn't be accessible.</li>\n<li>Use an <code>&lt;a&gt;</code> element in each table cell (<code>&lt;td&gt;</code>)\nbecause that's not the same thing\nand this wouldn't be accessible either (duplicate links).</li>\n</ul>\n<p>So I use the <code>::after</code> pseudo-element trick\nto expand a link's clickable area to cover the whole table row:</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>table</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>thead</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>tr</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>th</span><span class=\"token punctuation\">></span></span>Name<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>th</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>th</span><span class=\"token punctuation\">></span></span>Age<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>th</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>th</span><span class=\"token punctuation\">></span></span>Eye color<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>th</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>th</span><span class=\"token punctuation\">></span></span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>span</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>visually-hidden<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>Read more<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>span</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>th</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>tr</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>thead</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>tbody</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>tr</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>td</span><span class=\"token punctuation\">></span></span>John<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>td</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>td</span><span class=\"token punctuation\">></span></span>22<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>td</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>td</span><span class=\"token punctuation\">></span></span>Blue<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>td</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>td</span><span class=\"token punctuation\">></span></span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">aria-label</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>Read more about John<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>#<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>→<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>td</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>tr</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token comment\">&lt;!-- ... --></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>tbody</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>table</span><span class=\"token punctuation\">></span></span>\n\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>style</span><span class=\"token punctuation\">></span></span><span class=\"token style\"><span class=\"token language-css\">\n  <span class=\"token selector\">tr</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">position</span><span class=\"token punctuation\">:</span> relative<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token selector\">a::after</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">content</span><span class=\"token punctuation\">:</span> <span class=\"token string\">''</span><span class=\"token punctuation\">;</span>\n    <span class=\"token property\">inset</span><span class=\"token punctuation\">:</span> 0<span class=\"token punctuation\">;</span>\n    <span class=\"token property\">position</span><span class=\"token punctuation\">:</span> absolute<span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">/* Debug outline */</span>\n    <span class=\"token property\">outline</span><span class=\"token punctuation\">:</span> 2px solid hotpink<span class=\"token punctuation\">;</span>\n    <span class=\"token property\">outline-offset</span><span class=\"token punctuation\">:</span> -3px<span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>style</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<p><a href=\"https://flems.io/#0=N4IgzgpgNhDGAuEAmIBcIB0sxhAGhADMBLGXVAbVADsBDAWwjUwAt56p8RYB7axfswA8LAIwA+AAoQeABxhCA9GPEAdauqHxaAIxhrqAAkNaWEWkgPHjWgE5XrN+C0O0oxAObUAvKpAxCeD9xADkGCCVnB0dTV3cvXxBbTzZggEEPCMUo9UcnFzdPHz8AoJBxAFEATwhDXigeW0iWaOtYwoSSiEDg3LybMFlaI1goWhxEgDdiMABXNygqgFoWYiQkCA1ygCVzJEN6RqzB4dabbJa+8-h7Pua9hy0dHiQq1rszk3hLACkeFmokUsVza3ziRUSyQ8qXKACZYUDPlpLAAhKCzLLfJFYkExWiuZK0JZjHTQRK7CwHI6uZ6zeCGP4AvyGFi2bqJADEwUASYRKWhI7LAoxtbK3YVOMX9ZGhYiwADWiNxX32HWKSRSZXEAGYAJyK8Wg1G2HgAd0BguxQv6NnxtEJxN0ZL8FP2hzZNJ4dMMIVlcuZrPZfi55V5in5Sv1MVF7xulvEaUY7kxVryyPBnXV0M1ogAbJHU1iAOJszb5mI4g14gnEIkkp0gF1U926T30hPQYgQf1swicnl8gUVqOxu7ZZ6vAyRXT6dRcSAwBDEPjkEAAFgADKh1yAAL54GjhZhYHBcXj8TbwZg3QzAPqyHhgYjwJfUVCGNljZ+TCAAbnUO-UdRaFQVBaECCBbBvPozwEeA3z8Pw-2FYhqEgODDHXJDjHvR9nz4N9dDAHh0UQJC+kUAAqQwABEIB0WYPEMVt3GoWoKMUPpmJQiA31hWQAA9DCI9x9hYHh4FkFC5Swpi6RYiAlh4QhCDQt8li1ASkIAjRqEowwKmofYlPfaAIEmYZ6TAeAqjIQx2MA-hp1qW9hWeWwNlsJZ6jGWRIDfbzaF839-wcm5UBYcYAApwp4b9bAASkMMEXOMHRaHlDxjVmQyvOIxo3w5CAiq00KWDwdRkrvCwkBQjw33XDAAFY2XoEqdOccr+FsVBqHE6KxisrzVigJBEsq1zGg8pZnngeAeHoN9RAEoTiLWNq51Mxdl2YLUtS3XcAF0dyAA\" class=\"link link-external\">Demo of the problem on Flems.io.</a></p>\n<p>(This is a crude-ish example;\nsee e.g. <a href=\"https://adrianroselli.com/2023/11/dont-turn-a-table-into-an-aria-grid-just-for-a-clickable-row.html\" class=\"link link-external\"><em>Don't Turn a Table into an ARIA Grid Just for a Clickable Row</em> by Adrian Roselli</a>\nfor tips on accessibility.)</p>\n<p>Works well e.g. in Firefox:</p>\n<p><img src=\"./problem-firefox.png\" alt=\"Screenshot of Firefox showing that the problem is not a problem: the links' debug outlines cover the table rows.\"></p>\n<p>But Safari doesn't support <code>position: relative</code> on table rows,\nso the <code>::after</code> element is way too big – it covers the whole viewport:</p>\n<p><img src=\"./problem-safari.png\" alt=\"Screenshot of Safari showing the problem: the links' debug outlines cover the whole viewport.\"></p>\n<p>In effect, clicking anywhere on the page (above the fold if the page is scrollable) triggers the link on the last table row!</p>\n<h2 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h2>\n<h3 id=\"step-1-alternative-for-position-relative\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-1-alternative-for-position-relative\">Step 1: alternative for <code>position: relative</code></a></h3>\n<pre aria-label=\"Code block\" class=\"language-diff-css bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-css\"><span class=\"token prefix unchanged\"> </span> <span class=\"token selector\">tr</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"token deleted-sign deleted language-css\"><span class=\"token prefix deleted\">-</span>   <span class=\"token property\">position</span><span class=\"token punctuation\">:</span> relative<span class=\"token punctuation\">;</span>\n</span><span class=\"token inserted-sign inserted language-css\"><span class=\"token prefix inserted\">+</span>   <span class=\"token property\">transform</span><span class=\"token punctuation\">:</span> <span class=\"token function\">translate</span><span class=\"token punctuation\">(</span>0<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</span><span class=\"token unchanged language-css\"><span class=\"token prefix unchanged\"> </span> <span class=\"token punctuation\">}</span>\n</span></span></code></pre>\n<p>I found this from <a href=\"https://github.com/w3c/csswg-drafts/issues/1899\" class=\"link link-external\">a GitHub issue in the w3c/csswg-drafts repo: <em>[css-position][css-tables] position doesn't apply on <code>&lt;tr&gt;</code> either</em></a>,\nfrom <a href=\"https://github.com/w3c/csswg-drafts/issues/1899#issuecomment-338773780\" class=\"link link-external\">a comment by Oriol Brufau</a>.</p>\n<p><a href=\"https://github.com/w3c/csswg-drafts/issues/1899#issuecomment-1761309439\" class=\"link link-external\">Honza Kopecký commented</a>\nin Oct 2023 that &quot;both Safari and Chrome on iOS don't work and the hack doesn't help either,&quot;\nbut in my testing (Jun 2024) the hack does work in Safari on iPadOS v17.4.1.</p>\n<p>Two more relevant things that I found:</p>\n<ul>\n<li><a href=\"https://bugs.webkit.org/show_bug.cgi?id=240961\" class=\"link link-external\">WebKit bug report: <em><code>position: relative</code> doesn't work as expected on table row</em></a></li>\n<li><a href=\"https://stackoverflow.com/q/51843534/1079869\" class=\"link link-external\">Stack Overflow question: <em>Bug in most browsers?: Ignoring position relative on 'tbody', 'tr' and 'td'?</em></a></li>\n</ul>\n<h3 id=\"step-2-still-too-big\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-2-still-too-big\">Step 2: still too big</a></h3>\n<p>Now the links' clickable areas are smaller,\nbut still too big in Safari;\nthey are as tall as the whole table:</p>\n<p><img src=\"./step-2-safari.png\" alt=\"Screenshot of Safari showing that step 1 fixed the problem only partially: the links' debug outlines are still too big.\"></p>\n<p>In effect, the last link's clickable area expands below the table,\nbut otherwise the table rows are properly clickable.</p>\n<p>I found a solution from\n<a href=\"https://github.com/w3c/csswg-drafts/issues/1899#issuecomment-1232707455\" class=\"link link-external\">a GitHub comment by Loz Calver</a>\nunder the aforementioned GitHub issue:</p>\n<blockquote>\n<p>If your only issue is absolutely positioned elements overflowing the table-row (e.g. absolutely positioned anchors to make a full row clickable),\nanother possible workaround is to use <code>tr { clip-path: inset(0); }</code>.\nIt causes some strange paint side-effects in Safari,\nlike borders sometimes disappearing,\nbut it does at least clip child elements as expected.</p>\n</blockquote>\n<p>So:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-css bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-css\"><span class=\"token prefix unchanged\"> </span> <span class=\"token selector\">tr</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix unchanged\"> </span>   <span class=\"token property\">transform</span><span class=\"token punctuation\">:</span> <span class=\"token function\">translate</span><span class=\"token punctuation\">(</span>0<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</span><span class=\"token inserted-sign inserted language-css\"><span class=\"token prefix inserted\">+</span>   <span class=\"token property\">clip-path</span><span class=\"token punctuation\">:</span> <span class=\"token function\">inset</span><span class=\"token punctuation\">(</span>0<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</span><span class=\"token unchanged language-css\"><span class=\"token prefix unchanged\"> </span> <span class=\"token punctuation\">}</span>\n</span></span></code></pre>\n<h3 id=\"step-3-focus-outlines\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-3-focus-outlines\">Step 3: focus outlines</a></h3>\n<p>Because of the <code>clip-path</code> used in step 2,\nmake sure you use negative <code>outline-offset</code> for the table rows' focus styles,\nor the focus styles will also be clipped and invisible:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-css bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-css\"><span class=\"token prefix unchanged\"> </span> <span class=\"token selector\">tr</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token property\">transform</span><span class=\"token punctuation\">:</span> <span class=\"token function\">translate</span><span class=\"token punctuation\">(</span>0<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token property\">clip-path</span><span class=\"token punctuation\">:</span> <span class=\"token function\">inset</span><span class=\"token punctuation\">(</span>0<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token prefix unchanged\"> </span> <span class=\"token punctuation\">}</span>\n</span><span class=\"token inserted-sign inserted language-css\"><span class=\"token prefix inserted\">+</span> <span class=\"token selector\">tr:focus-within</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix inserted\">+</span>   <span class=\"token property\">outline</span><span class=\"token punctuation\">:</span> 2px solid hotpink<span class=\"token punctuation\">;</span>\n<span class=\"token prefix inserted\">+</span>   <span class=\"token property\">outline-offset</span><span class=\"token punctuation\">:</span> -2px<span class=\"token punctuation\">;</span> <span class=\"token comment\">/* Needs to be negative because of the `clip-path` above */</span>\n<span class=\"token prefix inserted\">+</span> <span class=\"token punctuation\">}</span>\n</span>\n<span class=\"token inserted-sign inserted language-css\"><span class=\"token prefix inserted\">+</span> <span class=\"token selector\">a:focus</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix inserted\">+</span>   <span class=\"token property\">outline</span><span class=\"token punctuation\">:</span> 0<span class=\"token punctuation\">;</span> <span class=\"token comment\">/* The focus styles are on the table row */</span>\n<span class=\"token prefix inserted\">+</span> <span class=\"token punctuation\">}</span>\n</span><span class=\"token unchanged language-css\"><span class=\"token prefix unchanged\"> </span> <span class=\"token selector\">a::after</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix unchanged\"> </span>   <span class=\"token property\">content</span><span class=\"token punctuation\">:</span> <span class=\"token string\">''</span><span class=\"token punctuation\">;</span>\n<span class=\"token prefix unchanged\"> </span>   <span class=\"token property\">inset</span><span class=\"token punctuation\">:</span> 0<span class=\"token punctuation\">;</span>\n<span class=\"token prefix unchanged\"> </span>   <span class=\"token property\">position</span><span class=\"token punctuation\">:</span> absolute<span class=\"token punctuation\">;</span>\n</span>\n<span class=\"token deleted-sign deleted language-css\"><span class=\"token prefix deleted\">-</span>   <span class=\"token comment\">/* Debug outline */</span>\n<span class=\"token prefix deleted\">-</span>   <span class=\"token property\">outline</span><span class=\"token punctuation\">:</span> 2px solid hotpink<span class=\"token punctuation\">;</span>\n<span class=\"token prefix deleted\">-</span>   <span class=\"token property\">outline-offset</span><span class=\"token punctuation\">:</span> -3px<span class=\"token punctuation\">;</span>\n</span><span class=\"token unchanged language-css\"><span class=\"token prefix unchanged\"> </span> <span class=\"token punctuation\">}</span>\n</span></span></code></pre>\n<h3 id=\"final-code\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#final-code\">Final code</a></h3>\n<pre aria-label=\"Code block\" class=\"language-css bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token selector\">tr</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">/*\n    Safari doesn't support `position: relative` on `&lt;tr>` elements,\n    but these two properties can be used as an alternative.\n    Source: https://mtsknn.fi/blog/relative-tr-in-safari/\n  */</span>\n  <span class=\"token property\">transform</span><span class=\"token punctuation\">:</span> <span class=\"token function\">translate</span><span class=\"token punctuation\">(</span>0<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token property\">clip-path</span><span class=\"token punctuation\">:</span> <span class=\"token function\">inset</span><span class=\"token punctuation\">(</span>0<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token selector\">tr:focus-within</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property\">outline</span><span class=\"token punctuation\">:</span> 2px solid hotpink<span class=\"token punctuation\">;</span>\n  <span class=\"token property\">outline-offset</span><span class=\"token punctuation\">:</span> -2px<span class=\"token punctuation\">;</span> <span class=\"token comment\">/* Needs to be negative because of the `clip-path` above */</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token selector\">a:focus</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property\">outline</span><span class=\"token punctuation\">:</span> 0<span class=\"token punctuation\">;</span> <span class=\"token comment\">/* The focus styles are on the table row */</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token selector\">a::after</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property\">content</span><span class=\"token punctuation\">:</span> <span class=\"token string\">''</span><span class=\"token punctuation\">;</span>\n  <span class=\"token property\">inset</span><span class=\"token punctuation\">:</span> 0<span class=\"token punctuation\">;</span>\n  <span class=\"token property\">position</span><span class=\"token punctuation\">:</span> absolute<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p><a href=\"https://flems.io/#0=N4IgzgpgNhDGAuEAmIBcIB0sxhAGhADMBLGXVAbVADsBDAWwjUwAt56p8RYB7axfswA8LAIwA+AAoQeABxhCA9GPEAdauqHxaAIxhrqAAkNaWEWkgPHjWgE5XrN+C0O0oxAObUAvKpAxCeD9xADkGCCVnB0dTV3cvXxBbTzZggEEPCMUo9UcnFzdPHz8AoJBxAFEATwhDXigeW0iWaOtYwoSSiEDg3LybMFlaI1goWhxEgDdiMABXNygqgFoWYiQkCA1ygCVzJEN6RqzB4dabbJa+8-h7Pua9hy0dHiQq1rszk3hLACkeFmokUsVza3ziRUSyQ8qXKACZYUDPlpLAAhKCzLLfJFYkExWiuZK0JZjHTQRK7CwHI6uZ6zeCGP4AvyGFi2bqJADEwUASYRKWhI7LAoxtbK3YVOMX9ZGhYiwADWiNxX32HWKSRSZXEAGYAJyK8Wg1G2HgAd0BguxQv6NnxtEJxN0ZL8FP2hzZNJ4dMMIVlcuZrPZfi55V5in5Sv1MVF7xulvEaUY7kxVryyPBnXV0M1ogAbJHU1iAOJszb5mI4g14gnEIkkp0gF1U926T30hPQYgQf1swicnl8gUVqOxu7ZZ6vAyRXT6dRcSAwBDEPjkEAAFgADKh1yAAL54GjhZhYHBcXj8TbwZg3QzAPqKABUIIAyrRCHbiIYkDwIGBqAByek5lkWRGnpAADECwGIeAl2oVBDDZMYYMmCAwMMPhDDAj40OgCBGH4MA8BBHQvWcH9angE0eEMWRjVkCBbBgn86mGQxSUMWZIBVMBXCMNxEFsOhkIgDBn09WxYAgeC2HgWQwFQRRFHoeAwDlahqAwEhFD0HgPEURDaGEpYbiWYhqCWMBX3fRQ+nvGzhRuYYwEIRp6HgxzqDAJCIAACnXABKABuPpRmIWQliGZx4LMyB4D8oL1B3dQblQFzYE4pYTWg1YjFvYVW3cagpMMWFZAAD0MMAeHcfYWB4WSzLlYL8rpQqICWHhCEIWL4KWUqysCwwH29CBkB4+BqPYoqPEM4gULYuBaE42pOsMMjMNC8LIpYNCW3muzEvUdRaFSnh0p4vLjAKszivXQbhoAFTMQw0s4yr4CqMgCRWox1u0PRamNE1DAO6gkroVBUFfASbxCvgBHgeC-D8ZrjBiiBEcMO6+kg6DYPg3QqvRRBmvB9Rhoqah9lWxCIEmYZAI+r7QeS6dakutjGg2WwlnqMY5OKvnaAF0mjv4WxUBYcYfMlngUNsfy1v2DmdFoeUPGNWYqd56rGngjlRogUWNH4FgiP4ZWcYsJAzI8eD1wwABWNl6GN5KzeSiXqHqmWxjAeBedWKAkEVsEVa5hilmeeAJrcwxRHKyrqrWY251wxdl2YVdYS3XcAF0dyAA\" class=\"link link-external\">Demo of the final code on Flems.io.</a></p>\n<p>Tested with:</p>\n<ul>\n<li>Edge v125 on Mac</li>\n<li>Firefox v126 on Mac</li>\n<li>Safari v16.6 on Mac</li>\n<li>Safari on iPadOS v17.4.1</li>\n</ul>\n","summary":"This is a problem if you want whole table rows to be clickable.\nLuckily there's a (hacky) workaround.","date_published":"2024-06-11T12:00:00+03:00","tags":["CSS","Safari"]},{"id":"https://mtsknn.fi/blog/sounds-like-a-plan/","url":"https://mtsknn.fi/blog/sounds-like-a-plan/","title":"\"Slap\" means \"sounds like a plan\"","content_html":"<p>I came up with this acronym myself,\nand I'm proud of it!</p>\n<p>I use it often when chatting and conversing with my fam &amp; friends.\nNot with my parents though, they wouldn't understand.</p>\n<p>Sometimes I go so far as to say &quot;slapety slap.&quot;</p>\n","summary":"I came up with this acronym myself,\nand I'm proud of it!","date_published":"2024-03-13T12:00:00+03:00","tags":["English"]},{"id":"https://mtsknn.fi/blog/dbeaver-azure-ad/","url":"https://mtsknn.fi/blog/dbeaver-azure-ad/","title":"How to use Azure AD authentication in DBeaver","content_html":"<p>Alternative title:\nHow to fix the &quot;Failed to load MSAL4J Java library for performing ActiveDirectoryPassword authentication&quot; error.</p>\n<p>I'm using the free Community Edition of <a href=\"https://dbeaver.io/\" class=\"link link-external\">DBeaver</a>,\nversion 23.3.5.</p>\n<p>In the &quot;Connection Settings&quot; pop-up window:</p>\n<ol>\n<li>\n<p>Select e.g. &quot;Active Directory – MFA&quot; as the authentication method and type your username</p>\n<p><img src=\"./connection-settings-window.png\" alt=\"\"></p>\n</li>\n<li>\n<p>Click <kbd>Driver Settings</kbd></p>\n</li>\n<li>\n<p>Choose the &quot;Libraries&quot; tab</p>\n<p><img src=\"./edit-driver-window.png\" alt=\"\"></p>\n</li>\n<li>\n<p>Click <kbd>Add Artifact</kbd> from the right</p>\n</li>\n<li>\n<p>Paste the following XML</p>\n<pre aria-label=\"Code block\" class=\"language-xml bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">&lt;!-- https://mvnrepository.com/artifact/com.microsoft.azure/msal4j --></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>dependency</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>groupId</span><span class=\"token punctuation\">></span></span>com.microsoft.azure<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>groupId</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>artifactId</span><span class=\"token punctuation\">></span></span>msal4j<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>artifactId</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>version</span><span class=\"token punctuation\">></span></span>1.14.3<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>version</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>dependency</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<p><img src=\"./edit-maven-artifact-window.png\" alt=\"\"></p>\n<ul>\n<li>Note: the latest version number can be found <a href=\"https://mvnrepository.com/artifact/com.microsoft.azure/msal4j\" class=\"link link-external\">from the URL in the XML comment</a>,\nthough the exact version you use might not matter that much</li>\n<li>I found this step from <a href=\"https://github.com/dbeaver/dbeaver/issues/15446#issuecomment-1097140996\" class=\"link link-external\">this GitHub comment</a></li>\n</ul>\n</li>\n<li>\n<p>Click <kbd>OK</kbd></p>\n</li>\n<li>\n<p>Click <kbd>Download/Update</kbd> from the right\nand <kbd>Download</kbd> in the opening pop-up window</p>\n</li>\n<li>\n<p>Click <kbd>OK</kbd></p>\n</li>\n<li>\n<p>Click <kbd>Test Connection ...</kbd> and authenticate in the opening browser window</p>\n</li>\n</ol>\n","summary":"Alternative title:\nHow to fix the \"Failed to load MSAL4J Java library for performing ActiveDirectoryPassword authentication\" error.","date_published":"2024-02-28T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/tricky-mailto-links/","url":"https://mtsknn.fi/blog/tricky-mailto-links/","title":"`mailto:` links with `target=\"_blank\"` are surprisingly tricky","content_html":"<p>Only because Firefox ignores <code>target=&quot;_blank&quot;</code> in <code>mailto:</code> links.</p>\n<p>Let's say you have an email link that opens in a new tab:</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>mailto:hello@example.com<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">target</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>_blank<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>Email me<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<ul>\n<li>✅ Clicking the link in Edge works correctly (opens an email client, e.g. Outlook).</li>\n<li>✅ Clicking the link in Safari works correctly (opens an email client).</li>\n<li>✅ If <a href=\"https://support.mozilla.org/en-US/kb/change-program-used-open-email-links\" class=\"link link-external\">Firefox is configured to open an email client when clicking <code>mailto:</code> links</a>,\nclicking the link works correctly (opens an email client).</li>\n<li>❌ If Firefox is configured to open a webmail (e.g. Gmail) when clicking <code>mailto:</code> links,\nclicking the link opens the webmail in the current tab –\n<a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=646552\" class=\"link link-external\">Firefox ignores <code>target=&quot;_blank&quot;</code> in <code>mailto:</code> links because of a long-standing bug</a>.\nThis buggy behavior can be quite annoying.</li>\n</ul>\n<p>(I wanted to test how Edge and Safari behave\nif they have been configured to open a webmail when clicking <code>mailto:</code> links,\nbut configuring this setting in those browsers turned out to be too difficult.<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup> smh)</p>\n<p>Based on <a href=\"https://stackoverflow.com/a/24854461/1079869\" class=\"link link-external\">this Stack Overflow answer</a>,\nI tried this kind of <code>onClick</code> trickery:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span>\n  <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>mailto:hello@example.com<span class=\"token punctuation\">\"</span></span>\n  <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">event</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    event<span class=\"token punctuation\">.</span><span class=\"token function\">preventDefault</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    window<span class=\"token punctuation\">.</span><span class=\"token function\">open</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'mail'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span></span>\n  <span class=\"token comment\">// No `target=\"_blank\"`</span>\n<span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n  Email me\n</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<p>✅ If Firefox is configured to open a webmail when clicking <code>mailto:</code> links,\nclicking the link works now correctly:\nthe webmail is opened in a new tab.</p>\n<p>But the UX is degraded in all other cases:</p>\n<ul>\n<li>❌ Firefox, configured to open <code>mailto:</code> links in an email client: the new empty tab is left open.</li>\n<li>❌ Edge: the new empty tab is immediately closed, which is great, but causes some jarring flashing.</li>\n<li>❌ Safari: opens a new tab with a confirm dialog: &quot;This website has been blocked from automatically composing an email.&quot;\n<ul>\n<li>Clicking &quot;Allow&quot; opens an email client, but the new empty tab is left open.</li>\n<li>Clicking &quot;Ignore&quot; leaves the new empty tab open.</li>\n</ul>\n</li>\n</ul>\n<p>I was tempted to check if the user's browser is Firefox,\nand only then use the <code>onClick</code> trick.\nBut trying to detect the user's browser\nwould open another can of worms.</p>\n<p>In the end I ended up doing nothing about this,\nwhich sucks for some Firefox users,\nbut I don't know what I could do that wouldn't degrade the UX of any other users. 🤔</p>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>I did find <a href=\"https://christianheilmann.com/2020/07/20/quick-tip-how-to-make-mailto-links-open-in-gmail-in-microsoft-edge/\" class=\"link link-external\">instructions how configure Edge to open <code>mailto:</code> links in Gmail</a>,\nbut it would require logging in to Gmail,\nand I don't want to do that,\nbecause Edge is a shitty browser\n(see e.g.\n<a href=\"https://thomask.sdf.org/blog/2023/03/18/the-dark-defaults-of-microsoft-edge.html\" class=\"link link-external\"><em>The dark defaults of Microsoft Edge</em></a>\nand\n<a href=\"https://www.neowin.net/news/edge-sends-images-you-view-online-to-microsoft-here-is-how-to-disable-that/\" class=\"link link-external\"><em>Edge sends images you view online to Microsoft</em></a>).\nI use Edge only for testing and occasionally for some web development.\nFirefox ftw. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"Only because Firefox ignores `target=\"_blank\"` in `mailto:` links.","date_published":"2024-02-19T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/burp-suite-ux-bad/","url":"https://mtsknn.fi/blog/burp-suite-ux-bad/","title":"Fix all requests timeouting in Burp Suite's Repeater","content_html":"<p>Make sure you are not missing two empty lines after the request headers.\nThere's a UX lesson here.</p>\n<h2 id=\"problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problem\">Problem</a></h2>\n<p>I was using <a href=\"https://portswigger.net/burp\" class=\"link link-external\">Burp Suite</a>\n(Community Edition, version 2023.12.1.5)\nfor the first time.</p>\n<p><a href=\"https://portswigger.net/burp/documentation/desktop/getting-started/reissuing-http-requests\" class=\"link link-external\">Sending requests in the Repeater tab</a>\nresulted only in timeouts:</p>\n<pre aria-label=\"Code block\" class=\"language-http bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token response-status\"><span class=\"token http-version property\">HTTP/1.1</span> <span class=\"token status-code number\">408</span> <span class=\"token reason-phrase string\">Request Time-out</span></span>\n<span class=\"token header\"><span class=\"token header-name keyword\">Content-length</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">110</span></span>\n<span class=\"token header\"><span class=\"token header-name keyword\">Cache-Control</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">no-cache</span></span>\n<span class=\"token header\"><span class=\"token header-name keyword\">Connection</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">close</span></span>\n<span class=\"token header\"><span class=\"token header-name keyword\">Content-Type</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">text/html</span></span>\n<span class=\"token text-html\">\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>html</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>body</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>408 Request Time-out<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\nYour browser didn't send a complete request in time.\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>body</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>html</span><span class=\"token punctuation\">></span></span>\n</span></span></code></pre>\n<h2 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h2>\n<p>Adding two empty lines after the request headers fixed it for me.</p>\n<p>Before:</p>\n<pre aria-label=\"Code block\" class=\"language-http bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token request-line\"><span class=\"token method property\">GET</span> <span class=\"token request-target url\">/wiki/Main_Page</span> <span class=\"token http-version property\">HTTP/1.1</span></span>\n<span class=\"token header\"><span class=\"token header-name keyword\">Host</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">en.wikipedia.org</span></span>\n<span class=\"token header\"><span class=\"token header-name keyword\">Accept</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">*/*</span></span>\n</span></code></pre>\n<p>After:</p>\n<pre aria-label=\"Code block\" class=\"language-http bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token request-line\"><span class=\"token method property\">GET</span> <span class=\"token request-target url\">/wiki/Main_Page</span> <span class=\"token http-version property\">HTTP/1.1</span></span>\n<span class=\"token header\"><span class=\"token header-name keyword\">Host</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">en.wikipedia.org</span></span>\n<span class=\"token header\"><span class=\"token header-name keyword\">Accept</span><span class=\"token punctuation\">:</span> <span class=\"token header-value\">*/*</span></span>\n\n\n</span></code></pre>\n<p>I found the solution and an explanation\nfrom <a href=\"https://forum.portswigger.net/thread/repeater-request-timeout-45df07ef58c\" class=\"link link-external\">a thread on Burp Suite User Forum</a>:</p>\n<blockquote>\n<p><strong>Andre:</strong>\nI found a solution to this. I was removing the 2 extra lines from the request when adding the new header. If I keep them, the requests go through. [...]</p>\n<p><strong>Diego:</strong>\nI was having the same issue, but like Andre said above, one of the blank lines of the request had been removed. Adding it back made requests in Repeater work again.</p>\n<p><strong>Uthman, PortSwigger Agent:</strong>\nThanks for your feedback. You can find more information on why this (<code>\\r\\n\\r\\n</code>) is required in the <a href=\"https://datatracker.ietf.org/doc/html/rfc2616#section-5\" class=\"link link-external\">RFC 2616 documentation</a>.</p>\n</blockquote>\n<h2 id=\"ux-lesson\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#ux-lesson\">UX lesson</a></h2>\n<p>That's terrible UX:</p>\n<ul>\n<li>The UI doesn't tell that the two empty lines are required.</li>\n<li>The problem (all requests timeouting) is confusing.\n<ul>\n<li>I bet most users are not so intimately familiar with RFC 2616\nthat they can right off the bat deduct that\n&quot;Oh, right, all requests seem to be timeouting,\nso I must have forgotten the two trailing newlines required in section 5 of RFC 2616!&quot;</li>\n</ul>\n</li>\n</ul>\n<p>How I would fix this UX issue:</p>\n<ul>\n<li>The UI should show a warning or an error if the empty lines are missing.</li>\n<li>Or better yet,\nthe app should add the missing empty lines automatically\nwhen the user sends a request.\n<ul>\n<li>To be fair,\nthe app seems to add the empty trailing lines automatically,\nbut inconsistently (maybe only when using HTTP/2).</li>\n</ul>\n</li>\n</ul>\n<p>I guess the &quot;UX lesson&quot; can be generalized like so:\ncommunicate obscure requirements to the user,\nand auto-fix issues where possible.</p>\n","summary":"Make sure you are not missing two empty lines after the request headers.\nThere's a UX lesson here.","date_published":"2024-02-18T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/video-games-2023/","url":"https://mtsknn.fi/blog/video-games-2023/","title":"Two great video games I played through in 2023","content_html":"<p>I managed to find time to play Desperados III and Lost in Play\n(and Before Your Eyes too a bit).</p>\n<h2 id=\"desperados-iii\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#desperados-iii\">Desperados III</a></h2>\n<p><img src=\"./desperados-iii.jpg\" alt=\"Screenshot of Desperados III.\"></p>\n<p>(Screenshot taken from the game's official website.)</p>\n<blockquote>\n<p>Desperados III is a story-driven, hardcore tactical stealth game, set in a ruthless Wild West scenario.</p>\n</blockquote>\n<ul>\n<li>✅ Great visuals</li>\n<li>✅ Nice gameplay (tactical stealth game)</li>\n<li>✅ Plenty of content – Steam shows that I spent 27.5 hours playing the game</li>\n</ul>\n<p>I have previously also played the game's predecessor,\n<a href=\"https://store.steampowered.com/app/418240/Shadow_Tactics_Blades_of_the_Shogun/\" class=\"link link-external\">Shadow Tactics</a>,\nwhich is from the same game studio\nand has a samurai theme.\nIt was a great game too.</p>\n<p>Looks like the game studio has released a third similar game last year –\n<a href=\"https://store.steampowered.com/app/1545560/Shadow_Gambit_The_Cursed_Crew/\" class=\"link link-external\">Shadow Gambit: The Cursed Crew</a> –\nwhich has a &quot;cursed pirate crew&quot; theme.\nMaybe not the best theme for me,\nbut at least the gameplay should be great.</p>\n<p>➡️ <a href=\"https://desperadosgame.com/\" class=\"link link-external\">Official website of Desperados III</a></p>\n<h2 id=\"lost-in-play\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#lost-in-play\">Lost in Play</a></h2>\n<p><img src=\"./lost-in-play.png\" alt=\"Screenshot of Lost in Play.\"></p>\n<p>(Screenshot taken from the game's official website.)</p>\n<blockquote>\n<p>Solve puzzles, riddles, and embark on an exciting journey of discovery! Join Toto and Gal as they explore dreamscapes and befriend magical creatures. Lost in their imagination, the brother and sister must stick together and solve puzzles to find their way home.</p>\n</blockquote>\n<ul>\n<li>✅ Fantastic music – when I first opened the game,\nI got stuck in the main menu for a while because I was jamming to the music!\n(The characters' voices are also excellent)</li>\n<li>✅ Fantastic visuals – I really like this kind of art;\nthe game feels like a great cartoon movie,\nexcept you are in control</li>\n<li>✅ Fun puzzles that got me thinking, but not too hard</li>\n<li>✅ Good, childish humor</li>\n</ul>\n<p>Steam shows I spent 4.7 hours playing the game,\nso it's a short game, but excellent!</p>\n<p>➡️ <a href=\"https://www.happyjuice.games/\" class=\"link link-external\">Official website of Lost in Play</a>\n(actually the game studio's website, but it doesn't have any subpages as of now)</p>\n<h2 id=\"also-before-your-eyes\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#also-before-your-eyes\">Also: Before Your Eyes</a></h2>\n<p><img src=\"./before-your-eyes.png\" alt=\"Screenshot of Before Your Eyes.\"></p>\n<p>(Screenshot taken from the game's official website.)</p>\n<blockquote>\n<p>Embark on an emotional first-person narrative adventure where you control the story – and affect its outcomes – with your real-life blinks. With this innovative technique you will fully immerse yourself in a world of memories, both joyous and heartbreaking, as your whole life flashes before your eyes.</p>\n</blockquote>\n<p><a href=\"https://hanki.dev/\" class=\"link link-external\">My bro</a> gifted me this game as a birthday present (PS thx!).</p>\n<p>Controlling the story with your &quot;real-life blinks&quot;\nmeans that you need a webcam (optional).\nCool and likely quite unique technique,\nbut a bit wonky and unreliable in the end,\nso I disabled it after a while.</p>\n<p>I played this game a few times\nbut dropped it eventually\nbecause it felt somehow felt so depressing. :D\nMaybe too much black all around (see the screenshot),\nand because spoiler (read backwards or <button id=\"reverse\">click to reverse</button>):</p>\n<blockquote>\n<p>|: hcum oot deugra stnerap s'tsinogatorp ehT</p>\n</blockquote>\n<script>\n  reverse.onclick = function () {\n    const blockquote = reverse.parentElement.nextElementSibling\n    blockquote.textContent = [...blockquote.textContent].reverse().join('')\n  }\n</script>\n<p>➡️ <a href=\"https://www.beforeyoureyesgame.com/\" class=\"link link-external\">Official website of Before Your Eyes</a>\n(stupid site because there's zero textual information about the game,\nand the <code>&lt;meta name=&quot;description&quot;&gt;</code> is empty too!)</p>\n","summary":"I managed to find time to play Desperados III and Lost in Play\n(and Before Your Eyes too a bit).","date_published":"2024-02-03T12:00:00+03:00","tags":["Games"]},{"id":"https://mtsknn.fi/blog/fiction-books-2023/","url":"https://mtsknn.fi/blog/fiction-books-2023/","title":"Fiction books I read and liked in 2023","content_html":"<p>I started a new hobby in 2023: reading fiction books!\nI started with fantasy: Harry Potter, Mistborn, and The Stormlight Archive.</p>\n<h2 id=\"harry-potter\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#harry-potter\">Harry Potter</a></h2>\n<p>After several years of not reading fiction books (almost) at all,\nI started my new reading hobby\nwith this classic heptalogy (= 7-book series)\nby J. K. Rowling.</p>\n<p>I have written about Harry Potter before:\n<a href=\"/blog/harry-potter/\" class=\"link\"><em>I read all 7 seven Harry Potter books in 33 days</em></a>.</p>\n<h2 id=\"mistborn\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#mistborn\">Mistborn</a></h2>\n<p>This was a great series!\nI read all 7 books in the series (trilogy + novella + tetralogy (= 4-book series)).\nBrandon Sanderson is such a good writer.</p>\n<p>Three things that are better in Mistborn than in Harry Potter:</p>\n<ul>\n<li>The magic system is logical and there are clear rules,\nwhereas in Harry Potter the magic system feels quite random and chaotic (everything seems possible).</li>\n<li>Action scenes are described clearly in Mistborn,\nwhereas in Harry Potter some scenes are quite unclear,\nlike &quot;many spells were flying here and there&quot;\n(not a direct quote, but I recall especially one scene that was described very vaguely).</li>\n<li>There are multiple viewpoints,\nwhich brings out people's different personalities,\nwhereas in Harry Potter almost everything is written from the viewpoint of Harry Potter.</li>\n</ul>\n<h2 id=\"the-stormlight-archive\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-stormlight-archive\">The Stormlight Archive</a></h2>\n<p>Also by Brandon Sanderson.</p>\n<p>This series is much more heavy than Mistborn –\nthere are much more characters and places and things etc.</p>\n<p>I'm glad I read Mistborn before this one\nbecause had I started with this,\nthis would probably have been way too overwhelming.</p>\n<p>So far I have read books 1, 2 and 2.5 (novella),\nand I'm waiting for book 3 from the library.</p>\n<h2 id=\"dislikes\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#dislikes\">Dislikes</a></h2>\n<p>I don't want to be Negative Nelly,\nso I'll just quickly note why I didn't like some books:</p>\n<ul>\n<li>Hunger Games (trilogy)\n<ul>\n<li>Silly premise</li>\n<li>Tacky romance</li>\n<li>First-person perspective (I seem to prefer third-person perspective)</li>\n<li>Present tense (feels weird; I prefer past tense)</li>\n<li>Spoiler (read backwards or <button id=\"reverse\">click to reverse</button>):\n<blockquote>\n<p>gnitirw yzal ekil tlef dna gniyonna saw hcihw ,tnemom lacitirc yrev a ni drawrof deppiks yrots eht tniop eno tA</p>\n</blockquote>\n<script>\n  reverse.onclick = function () {\n    const blockquote = reverse.nextElementSibling\n    blockquote.textContent = [...blockquote.textContent].reverse().join('')\n  }\n</script>\n</li>\n<li>I read the whole trilogy despite starting to dislike it quite soon after starting :D :|</li>\n</ul>\n</li>\n<li>The House in the Cerulean Sea (novel)\n<ul>\n<li>I had quite high expectations for this\nas I had read that this is a good novel to read after Harry Potter</li>\n<li>But this was too slow/boring,\nso I dropped this after a few dozen pages</li>\n</ul>\n</li>\n<li>Malazan Book of the Fallen (series)\n<ul>\n<li>I had read that this is a very heavy series;\nsomeone wrote that you need to read ~300 pages before you <em>might</em> start to get a grip of the plot</li>\n<li>So yeah, this indeed felt too heavy,\nso I dropped this after just a few dozen pages :D</li>\n</ul>\n</li>\n<li>Assassin's Apprentice (series)\n<ul>\n<li>First-person perspective (I seem to prefer third-person perspective)</li>\n<li>Too slow/boring beginning,\nso I dropped this after a few dozen pages</li>\n</ul>\n</li>\n</ul>\n","summary":"I started a new hobby in 2023: reading fiction books!\nI started with fantasy: Harry Potter, Mistborn, and The Stormlight Archive.","date_published":"2024-01-31T12:00:00+03:00","tags":["Books"]},{"id":"https://mtsknn.fi/blog/ublock-origin-style-operator/","url":"https://mtsknn.fi/blog/ublock-origin-style-operator/","title":"Tweaking website styles with uBlock Origin filters","content_html":"<p>Use the <code>:style()</code> operator in a uBlock Origin filter\nto apply CSS styles to the targeted elements.</p>\n<p>Here's how I use the <a href=\"https://github.com/gorhill/uBlock/wiki/Static-filter-syntax#subjectstylearg\" class=\"link link-external\"><code>:style()</code> operator</a>:</p>\n<ul>\n<li>Underline links on <a href=\"https://bazqux.com/\" class=\"link link-external\">BazQux (RSS reader)</a>.</li>\n<li>Hide points on <a href=\"https://hckrnews.com/\" class=\"link link-external\">hckr news</a>.</li>\n<li>Highlight visited comment links on hckr news.</li>\n</ul>\n<p>Here are the filters (under the &quot;My filters&quot; tab in the uBlock Origin dashboard):</p>\n<pre aria-label=\"Code block\" class=\"language-css bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">! underline links\nbazqux.com##.post .mtext <span class=\"token property\">a</span><span class=\"token punctuation\">:</span><span class=\"token function\">style</span><span class=\"token punctuation\">(</span><span class=\"token property\">text-decoration</span><span class=\"token punctuation\">:</span> underline <span class=\"token important\">!important</span><span class=\"token punctuation\">)</span>\n\n! hide points\nhckrnews.com##.entry.row <span class=\"token property\">a</span><span class=\"token punctuation\">:</span>first-child <span class=\"token property\">span</span><span class=\"token punctuation\">:</span><span class=\"token function\">style</span><span class=\"token punctuation\">(</span><span class=\"token property\">opacity</span><span class=\"token punctuation\">:</span> 0<span class=\"token punctuation\">)</span>\n\n! highlight visited comment links\nhckrnews.com##.entry.row <span class=\"token property\">a</span><span class=\"token punctuation\">:</span><span class=\"token property\">first-child</span><span class=\"token punctuation\">:</span><span class=\"token function\">style</span><span class=\"token punctuation\">(</span><span class=\"token property\">outline</span><span class=\"token punctuation\">:</span> 20px solid white<span class=\"token punctuation\">;</span> <span class=\"token property\">outline-offset</span><span class=\"token punctuation\">:</span> -20px<span class=\"token punctuation\">)</span>\nhckrnews.com##.entry.row <span class=\"token property\">a</span><span class=\"token punctuation\">:</span><span class=\"token property\">first-child</span><span class=\"token punctuation\">:</span><span class=\"token property\">visited</span><span class=\"token punctuation\">:</span><span class=\"token function\">style</span><span class=\"token punctuation\">(</span><span class=\"token property\">outline-color</span><span class=\"token punctuation\">:</span> #eee<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>I know there's also e.g. <a href=\"https://addons.mozilla.org/en-US/firefox/addon/styl-us/\" class=\"link link-external\">Stylus extension for Firefox</a>,\nbut the fewer extensions I need,\nthe better.</p>\n","summary":"Use the `:style()` operator in a uBlock Origin filter\nto apply CSS styles to the targeted elements.","date_published":"2023-11-13T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/handlebars-else-equals/","url":"https://mtsknn.fi/blog/handlebars-else-equals/","title":"\"Else equals\" in Handlebars (in SendGrid's Dynamic Templates)","content_html":"<p>There's a way to do an &quot;else equals&quot; check in Handlebars (in SendGrid's Dynamic Templates),\nthough it's undocumented and a bit tricky.</p>\n<h2 id=\"context\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#context\">Context</a></h2>\n<ul>\n<li><a href=\"https://docs.sendgrid.com/for-developers/sending-email/using-handlebars\" class=\"link link-external\">SendGrid's Dynamic Templates use Handlebars.</a></li>\n<li><a href=\"https://handlebarsjs.com/\" class=\"link link-external\">Handlebars is a templating language.</a></li>\n</ul>\n<h2 id=\"problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problem\">Problem</a></h2>\n<p>I wanted to do something like this:</p>\n<pre aria-label=\"Code block\" class=\"language-handlebars bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token handlebars language-handlebars\"><span class=\"token comment\">{{! Pseudo code, doesn't work }}</span></span>\n\n<span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token block keyword\">#if</span> <span class=\"token variable\">locale</span> <span class=\"token punctuation\">=</span><span class=\"token punctuation\">=</span> <span class=\"token string\">'en'</span><span class=\"token delimiter punctuation\">}}</span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>New article: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">articleNameEn</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">else</span> <span class=\"token variable\">if</span> <span class=\"token variable\">locale</span> <span class=\"token punctuation\">=</span><span class=\"token punctuation\">=</span> <span class=\"token string\">'sv'</span><span class=\"token delimiter punctuation\">}}</span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>Ny artikel: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">articleNameSv</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">else</span><span class=\"token delimiter punctuation\">}}</span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>Uusi artikkeli: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">articleNameFi</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token block keyword\">/if</span><span class=\"token delimiter punctuation\">}}</span></span>\n</span></code></pre>\n<p>But it wasn't quite that simple.</p>\n<h2 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h2>\n<h3 id=\"step-by-step\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-by-step\">Step by step</a></h3>\n<ul>\n<li><a href=\"https://handlebarsjs.com/guide/block-helpers.html#conditionals\" class=\"link link-external\">Handlebar's <code>#if</code> helper</a>\nchecks only the <a href=\"https://developer.mozilla.org/en-US/docs/Glossary/Truthy\" class=\"link link-external\">truthiness</a> of a value;\nyou can't use <code>==</code> or other operators,\nso <code>{{#if locale == 'en'}}</code> is invalid.</li>\n<li>You can create <a href=\"https://handlebarsjs.com/guide/#custom-helpers\" class=\"link link-external\">custom Handlebars helpers</a>.\nHowever,\nwhen using Handlebars in SendGrid,\n<a href=\"https://stackoverflow.com/q/71501851/1079869\" class=\"link link-external\">you are limited to SendGrid's Handlebars helpers</a>.</li>\n<li>Luckily\n<a href=\"https://docs.sendgrid.com/for-developers/sending-email/using-handlebars#equals\" class=\"link link-external\">SendGrid provides an <code>#equals</code> helper</a>:<pre aria-label=\"Code block\" class=\"language-handlebars bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token block keyword\">#equals</span> <span class=\"token variable\">locale</span> <span class=\"token string\">'en'</span><span class=\"token delimiter punctuation\">}}</span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>New article: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">articleNameEn</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">else</span><span class=\"token delimiter punctuation\">}}</span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>Uusi artikkeli: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">articleNameFi</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token block keyword\">/equals</span><span class=\"token delimiter punctuation\">}}</span></span>\n</span></code></pre>\n</li>\n<li>But whoops,\n<a href=\"https://stackoverflow.com/q/71427569/1079869\" class=\"link link-external\">Handlebars helpers create new scopes</a>,\nso <code>{{articleNameEn}}</code> and <code>{{articleNameFi}}</code> are &quot;empty.&quot;\nNeed to use the <code>@root</code> keyword:<pre aria-label=\"Code block\" class=\"language-diff-handlebars bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-handlebars\"><span class=\"token prefix unchanged\"> </span><span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token block keyword\">#equals</span> <span class=\"token variable\">locale</span> <span class=\"token string\">'en'</span><span class=\"token delimiter punctuation\">}}</span></span>\n</span><span class=\"token deleted-sign deleted language-handlebars\"><span class=\"token prefix deleted\">-</span>  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>New article: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">articleNameEn</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n</span><span class=\"token inserted-sign inserted language-handlebars\"><span class=\"token prefix inserted\">+</span>  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>New article: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">articleNameEn</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n</span><span class=\"token unchanged language-handlebars\"><span class=\"token prefix unchanged\"> </span><span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">else</span><span class=\"token delimiter punctuation\">}}</span></span>\n</span><span class=\"token deleted-sign deleted language-handlebars\"><span class=\"token prefix deleted\">-</span>  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>Uusi artikkeli: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">articleNameFi</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n</span><span class=\"token inserted-sign inserted language-handlebars\"><span class=\"token prefix inserted\">+</span>  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>Uusi artikkeli: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">articleNameFi</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n</span><span class=\"token unchanged language-handlebars\"><span class=\"token prefix unchanged\"> </span><span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token block keyword\">/equals</span><span class=\"token delimiter punctuation\">}}</span></span>\n</span></span></code></pre>\n</li>\n<li>And how to do &quot;else equals&quot;?\nThe documentation doesn't tell.\nBut <a href=\"https://docs.sendgrid.com/for-developers/sending-email/using-handlebars#basic-if-else-else-if\" class=\"link link-external\">&quot;else if&quot; is documented</a>,\nso let's try to imitate that:<pre aria-label=\"Code block\" class=\"language-handlebars bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token block keyword\">#equals</span> <span class=\"token variable\">locale</span> <span class=\"token string\">'en'</span><span class=\"token delimiter punctuation\">}}</span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>New article: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">articleNameEn</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">else</span> <span class=\"token variable\">equals</span> <span class=\"token variable\">locale</span> <span class=\"token string\">'sv'</span><span class=\"token delimiter punctuation\">}}</span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>Ny artikel: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">articleNameSv</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">else</span><span class=\"token delimiter punctuation\">}}</span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>Uusi artikkeli: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">articleNameFi</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token block keyword\">/equals</span><span class=\"token delimiter punctuation\">}}</span></span>\n</span></code></pre>\nThis code is syntactically valid and almost works:\n<ul>\n<li>If <code>locale</code> is <code>'en'</code>, the first branch is executed.</li>\n<li>But if <code>locale</code> is <code>'sv'</code>, the <em>third</em> branch is executed.\nSo the second branch (&quot;else equals&quot;) is never executed.</li>\n</ul>\n</li>\n<li>Aha! Same gotcha as above.\nBecause <a href=\"https://stackoverflow.com/q/71427569/1079869\" class=\"link link-external\">Handlebars is so eager to create new scopes</a>,\nneed to use the <code>@root</code> keyword here as well:<pre aria-label=\"Code block\" class=\"language-diff-handlebars bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-handlebars\"><span class=\"token prefix unchanged\"> </span><span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token block keyword\">#equals</span> <span class=\"token variable\">locale</span> <span class=\"token string\">'en'</span><span class=\"token delimiter punctuation\">}}</span></span>\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>New article: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">articleNameEn</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n</span><span class=\"token deleted-sign deleted language-handlebars\"><span class=\"token prefix deleted\">-</span><span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">else</span> <span class=\"token variable\">equals</span> <span class=\"token variable\">locale</span> <span class=\"token string\">'sv'</span><span class=\"token delimiter punctuation\">}}</span></span>\n</span><span class=\"token inserted-sign inserted language-handlebars\"><span class=\"token prefix inserted\">+</span><span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">else</span> <span class=\"token variable\">equals</span> <span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">locale</span> <span class=\"token string\">'sv'</span><span class=\"token delimiter punctuation\">}}</span></span>\n</span><span class=\"token unchanged language-handlebars\"><span class=\"token prefix unchanged\"> </span>  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>Ny artikel: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">articleNameSv</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">else</span><span class=\"token delimiter punctuation\">}}</span></span>\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>Uusi artikkeli: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">articleNameFi</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token block keyword\">/equals</span><span class=\"token delimiter punctuation\">}}</span></span>\n</span></span></code></pre>\n</li>\n</ul>\n<h3 id=\"final-code\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#final-code\">Final code</a></h3>\n<pre aria-label=\"Code block\" class=\"language-handlebars bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token block keyword\">#equals</span> <span class=\"token variable\">locale</span> <span class=\"token string\">'en'</span><span class=\"token delimiter punctuation\">}}</span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>New article: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">articleNameEn</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">else</span> <span class=\"token variable\">equals</span> <span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">locale</span> <span class=\"token string\">'sv'</span><span class=\"token delimiter punctuation\">}}</span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>Ny artikel: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">articleNameSv</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token variable\">else</span><span class=\"token delimiter punctuation\">}}</span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h1</span><span class=\"token punctuation\">></span></span>Uusi artikkeli: <span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token punctuation\">@</span><span class=\"token variable\">root</span><span class=\"token punctuation\">.</span><span class=\"token variable\">articleNameFi</span><span class=\"token delimiter punctuation\">}}</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h1</span><span class=\"token punctuation\">></span></span>\n<span class=\"token handlebars language-handlebars\"><span class=\"token delimiter punctuation\">{{</span><span class=\"token block keyword\">/equals</span><span class=\"token delimiter punctuation\">}}</span></span>\n</span></code></pre>\n","summary":"There's a way to do an \"else equals\" check in Handlebars (in SendGrid's Dynamic Templates),\nthough it's undocumented and a bit tricky.","date_published":"2023-11-10T12:00:00+03:00","tags":["Handlebars","SendGrid"]},{"id":"https://mtsknn.fi/blog/accept-vs-content-type/","url":"https://mtsknn.fi/blog/accept-vs-content-type/","title":"HTTP headers: Accept vs Content-Type","content_html":"<p>I sometimes forget what these two headers do in requests vs responses,\nso here's a refresher for me.</p>\n<dl>\n<dt><code>Accept</code> in request headers</dt>\n<dd>What type of response the client expects from the server.</dd>\n<dt><code>Accept</code> in response headers</dt>\n<dd>N/A</dd>\n<dt><code>Content-Type</code> in request headers</dt>\n<dd>The type of the request data (e.g. in a POST or PUT request).</dd>\n<dt><code>Content-Type</code> in response headers</dt>\n<dd>The type of the response data.</dd>\n</dl>\n<p>The <code>Content-Type</code> header is usually not needed\nif the request or response has no data\n(e.g. GET requests and 204 No Content responses).\nBut apparently <a href=\"https://stackoverflow.com/a/35722727/1079869\" class=\"link link-external\">&quot;some servers may require you to provide a <code>Content-Type</code> [header] in a request even if the request has no payload.&quot;</a></p>\n","summary":"I sometimes forget what these two headers do in requests vs responses,\nso here's a refresher for me.","date_published":"2023-11-09T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/firefox-slowness/","url":"https://mtsknn.fi/blog/firefox-slowness/","title":"Cmd-tabing to Firefox on Mac gets progressively slower","content_html":"<p>The longer Firefox is kept open,\nthe longer it takes for Firefox to receive focus after Cmd-tabing to it.\nAt some point it can take even about a second,\nwhich really kills the flow.</p>\n<p>I initially thought that maybe the problem is in the <a href=\"https://alt-tab-macos.netlify.app/\" class=\"link link-external\">AltTab app</a>,\nwhich is an essential Mac app by they way.\nBut nope, the problem exists even when not using AltTab.</p>\n<p>Then I thought that maybe it's some kind of memory leak,\ncaused/aggravated by keeping too many Firefox tabs open (a bad habit of mine).\nBut nope, <a href=\"https://addons.mozilla.org/en-US/firefox/addon/unload-tabs/\" class=\"link link-external\">unloading tabs to free resources</a>\ndoesn't help either.</p>\n<p>Only restarting Firefox helps,\nbut that's cumbersome (takes several seconds to do :D).</p>\n<p>Eventually I found\n<a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1808223\" class=\"link link-external\">a bug report on Bugzilla: <em>cmd-tab back to Firefox window on macOS is slow</em></a>.\nFrom a comment by Stephen A Pohl on Sep 25, 2023:</p>\n<blockquote>\n<p>This bug is next on my list of priorities.</p>\n</blockquote>\n<p>Yay! Now let's hope the fix is coming soon™.</p>\n","summary":"The longer Firefox is kept open,\nthe longer it takes for Firefox to receive focus after Cmd-tabing to it.\nAt some point it can take even about a second,\nwhich really kills the flow.","date_published":"2023-11-08T12:00:00+03:00","tags":["Mac"]},{"id":"https://mtsknn.fi/blog/js-default-exports-bad/","url":"https://mtsknn.fi/blog/js-default-exports-bad/","title":"JavaScript's default exports are a footgun","content_html":"<p>It's too easy to accidentally import a default export with a misleading name.</p>\n<p>Example:</p>\n<ul>\n<li>What I have wanted:<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> useSWR <span class=\"token keyword\">from</span> <span class=\"token string\">'swr'</span>\n<span class=\"token comment\">// or</span>\n<span class=\"token keyword\">import</span> useSWRImmutable <span class=\"token keyword\">from</span> <span class=\"token string\">'swr/immutable'</span>\n</span></code></pre>\n</li>\n<li>What I have accidentally done (more than once):<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> useSWRImmutable <span class=\"token keyword\">from</span> <span class=\"token string\">'swr'</span>\n<span class=\"token comment\">// or</span>\n<span class=\"token keyword\">import</span> useSWR <span class=\"token keyword\">from</span> <span class=\"token string\">'swr/immutable'</span>\n</span></code></pre>\nThen I have wasted time being confused\nby <a href=\"https://swr.vercel.app/\" class=\"link link-external\">SWR</a> not working like I expect it to.</li>\n</ul>\n<p>Named exports would prevent such silly mistakes:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// OK:</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> useSWR <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'swr'</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> useSWRImmutable <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'swr/immutable'</span>\n\n<span class=\"token comment\">// Error:</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> useSWRImmutable <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'swr'</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> useSWR <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'swr/immutable'</span>\n</span></code></pre>\n<p>This is why I think it's bad to use default exports (unless you have a good reason).</p>\n<p>Besides,\n<a href=\"https://www.olivare.net/blog/2023/death-to-default-exports\" class=\"link link-external\">default exports have other issues as well</a>.</p>\n","summary":"It's too easy to accidentally import a default export with a misleading name.","date_published":"2023-11-07T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/sorting-anti-symmetry/","url":"https://mtsknn.fi/blog/sorting-anti-symmetry/","title":"Moving zeros to the end of an array in JavaScript","content_html":"<p>Let's learn about anti-symmetry by solving this interview puzzle:\n&quot;Move all zeros of an array of integers to the end\nwhile maintaining the relative order of all non-zeros.&quot;</p>\n<p>Example:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">moveZeros</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">111</span><span class=\"token punctuation\">,</span> <span class=\"token number\">57</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">9</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=>      [1, 3,    111, 57,    5,    9, 0, 0, 0]</span>\n</span></code></pre>\n<p>Ok, ez because there's a great\n<a href=\"/blog/visual-mnemonic-for-array-sorting/\" class=\"link\">visual mnemonic for an array sorter's return values</a>,\nso we can simply™ <code>sort()</code> the values:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">moveZeros</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>numbers<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  numbers<span class=\"token punctuation\">.</span><span class=\"token function\">toSorted</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>a<span class=\"token punctuation\">,</span> b<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>a <span class=\"token operator\">===</span> <span class=\"token number\">0</span> <span class=\"token operator\">?</span> <span class=\"token number\">1</span> <span class=\"token operator\">:</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token function\">moveZeros</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">111</span><span class=\"token punctuation\">,</span> <span class=\"token number\">57</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">9</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=>      [1, 3,    111, 57,    5,    9, 0, 0, 0]</span>\n</span></code></pre>\n<p>I used the new <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted\" class=\"link link-external\"><code>toSorted()</code> method</a>\nto avoid mutating the original array.</p>\n<p>Seems to work with the test input,\nbut might break with other inputs,\nbecause the comparator function is not <em>anti-symmetric</em>.\nFrom <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort\" class=\"link link-external\"><code>sort()</code> on MDN</a>:</p>\n<blockquote>\n<p>[I]f a comparator only returns <code>1</code> and <code>0</code>,\nor only returns <code>0</code> and <code>-1</code>,\nit will not be able to sort reliably because <em>anti-symmetry</em> is broken.</p>\n</blockquote>\n<p>So we need to also check if <code>b === 0</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">moveZeros</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>numbers<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  numbers<span class=\"token punctuation\">.</span><span class=\"token function\">toSorted</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>a<span class=\"token punctuation\">,</span> b<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>a <span class=\"token operator\">===</span> <span class=\"token number\">0</span> <span class=\"token operator\">?</span> <span class=\"token number\">1</span> <span class=\"token operator\">:</span> b <span class=\"token operator\">===</span> <span class=\"token number\">0</span> <span class=\"token operator\">?</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span> <span class=\"token operator\">:</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Nested ternaries are icky though,\nplus returning <code>1</code> or <code>-1</code> is not very clear without comments.</p>\n<p>Final solution:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">moveZeros</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>numbers<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  numbers<span class=\"token punctuation\">.</span><span class=\"token function\">toSorted</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>a<span class=\"token punctuation\">,</span> b<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>a <span class=\"token operator\">===</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token operator\">+</span><span class=\"token number\">1</span> <span class=\"token comment\">//   a-></span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>b <span class=\"token operator\">===</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span> <span class=\"token comment\">// &lt;-a</span>\n    <span class=\"token keyword\">return</span> <span class=\"token number\">0</span> <span class=\"token comment\">// Keep order</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<hr>\n<p>Though I have to admit that\n<a href=\"https://gist.github.com/jkumara/d440aa3d3a1c6316eb9909e8c66bb941\" class=\"link link-external\">Juhani Kumara's solution using <code>reduceRight</code></a>\nis annoyingly clever!</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">move0</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">arr</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  arr<span class=\"token punctuation\">.</span><span class=\"token function\">reduceRight</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">acc<span class=\"token punctuation\">,</span> cur</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>acc<span class=\"token punctuation\">[</span>cur <span class=\"token operator\">?</span> <span class=\"token string\">'unshift'</span> <span class=\"token operator\">:</span> <span class=\"token string\">'push'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">(</span>cur<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> acc<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n","summary":"Let's learn about anti-symmetry by solving this interview puzzle:\n\"Move all zeros of an array of integers to the end\nwhile maintaining the relative order of all non-zeros.\"","date_published":"2023-11-06T12:00:00+03:00","tags":["JavaScript","TypeScript"]},{"id":"https://mtsknn.fi/blog/bulk-logging-hours-in-jira/","url":"https://mtsknn.fi/blog/bulk-logging-hours-in-jira/","title":"Logging hours to Jira's \"Work log\" in bulk","content_html":"<p>There's apparently no nice way to bulk-log hours in Jira.\nUse this One Weird Trick to do it anyway –\nJira consultants HATE it!</p>\n<h2 id=\"problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problem\">Problem</a></h2>\n<p>Jira's UI for logging hours (&quot;Work log&quot; / &quot;Time tracking&quot;) is clunky:</p>\n<ul>\n<li>It requires lots of mouse clicks and/or key presses.</li>\n<li>It gets very repetitive after a few times.</li>\n<li>The UI keeps changing from time to time, breaking my workflow.</li>\n</ul>\n<p>Officially <a href=\"https://community.atlassian.com/t5/Jira-questions/How-do-I-bulk-upload-work-hours-for-multiple-tickets-Log-time/qaq-p/1956146\" class=\"link link-external\">hours can apparently be logged in bulk by importing data from a CSV file</a>,\nbut that sounds clunky\nand doesn't work if your user account doesn't have permission to import data from CSV files.</p>\n<h2 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h2>\n<p>Overview:</p>\n<ol>\n<li>Track hours in a JS file.</li>\n<li>Log the hours by copy-pasting the code and running it in the browser's JS console.</li>\n</ol>\n<h3 id=\"tracking-hours\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#tracking-hours\">Tracking hours</a></h3>\n<p>Create a JavaScript file and keep track of hours with this fancy format:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// @ts-check</span>\n\n<span class=\"token comment\">/**\n * @type {Record&lt;\n *   `${'Mon' | 'Tue' | 'Wed' | 'Thu' | 'Fri'} 2023-11-${number}`,\n *   [ticketId: `FOO-${number}` | null, hours: number][]\n * >}\n */</span>\n<span class=\"token keyword\">const</span> items <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// ...</span>\n  <span class=\"token string-property property\">'Wed 2023-11-08'</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">[</span><span class=\"token string\">'FOO-123'</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1.5</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// pr fixes</span>\n    <span class=\"token punctuation\">[</span><span class=\"token string\">'FOO-333'</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// feature x</span>\n    <span class=\"token punctuation\">[</span><span class=\"token string\">'FOO-444'</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// pr review</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string-property property\">'Tue 2023-11-07'</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">[</span><span class=\"token string\">'FOO-222'</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// yarn install investigation</span>\n    <span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// backlog grooming</span>\n    <span class=\"token punctuation\">[</span><span class=\"token string\">'FOO-333'</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3.5</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// feature x</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string-property property\">'Mon 2023-11-06'</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token punctuation\">[</span><span class=\"token string\">'FOO-123'</span><span class=\"token punctuation\">,</span> <span class=\"token number\">7</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// nav revamp</span>\n    <span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0.5</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// daily</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Notes:</p>\n<ul>\n<li>Use JS instead of TS so that you can later copy-paste the code directly to the browser's JS console.</li>\n<li>The <code>// @ts-check</code> comment at the top lets you still get the benefits of TS;\nit makes VS Code nag at you if you get some values wrong.</li>\n<li>Adjust the date and ticket ID types accordingly;\ne.g. <code>2023-12-${number}</code> when it's December\nand <code>ABC-${number}</code> if your Jira ticket IDs start with <code>ABC-</code>.</li>\n<li>I'm using <code>null</code> instead of <code>FOO-${number}</code> for general stuff because I can't remember the &quot;general stuff&quot; ticket's ID.</li>\n<li>The code comments are for you so you'll remember later how you have been spending your time.</li>\n</ul>\n<h3 id=\"logging-hours\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#logging-hours\">Logging hours</a></h3>\n<h4>Initial setup</h4>\n<p>This needs to be done only once.</p>\n<ol>\n<li>\n<p>Open the browser's DevTools.</p>\n</li>\n<li>\n<p>Log hours manually in Jira (only one logging needed).</p>\n</li>\n<li>\n<p>Select the Network tab of DevTools\nand look for the XHR request that was just made.</p>\n</li>\n<li>\n<p>Right-click on the item and select &quot;Copy as Fetch.&quot;\nThe code might look something like this:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>\n  <span class=\"token string\">'https://jira.example.com/rest/internal/3/issue/FOO-123/worklog?adjustEstimate=new&amp;newEstimate=0m'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">credentials</span><span class=\"token operator\">:</span> <span class=\"token string\">'include'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">headers</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token string-property property\">'User-Agent'</span><span class=\"token operator\">:</span> <span class=\"token string\">'redacted for privacy lolz'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">Accept</span><span class=\"token operator\">:</span> <span class=\"token string\">'application/json,text/javascript,*/*'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">'Accept-Language'</span><span class=\"token operator\">:</span> <span class=\"token string\">'en-US,en;q=0.5'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\">:</span> <span class=\"token string\">'application/json'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">'X-Atlassian-Capability'</span><span class=\"token operator\">:</span> <span class=\"token string\">'ISSUE_VIEW'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">'Sec-Fetch-Dest'</span><span class=\"token operator\">:</span> <span class=\"token string\">'empty'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">'Sec-Fetch-Mode'</span><span class=\"token operator\">:</span> <span class=\"token string\">'cors'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">'Sec-Fetch-Site'</span><span class=\"token operator\">:</span> <span class=\"token string\">'same-origin'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">Pragma</span><span class=\"token operator\">:</span> <span class=\"token string\">'no-cache'</span><span class=\"token punctuation\">,</span>\n      <span class=\"token string-property property\">'Cache-Control'</span><span class=\"token operator\">:</span> <span class=\"token string\">'no-cache'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">referrer</span><span class=\"token operator\">:</span> <span class=\"token string\">'https://jira.example.com/browse/FOO-123'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">body</span><span class=\"token operator\">:</span>\n      <span class=\"token string\">'{\"timeSpent\":\"30m\",\"comment\":{\"type\":\"doc\",\"version\":1,\"content\":[]},\"started\":\"2023-10-30T08:00:00.000+0200\"}'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">method</span><span class=\"token operator\">:</span> <span class=\"token string\">'POST'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">mode</span><span class=\"token operator\">:</span> <span class=\"token string\">'cors'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span>\n</span></code></pre>\n</li>\n<li>\n<p>Add the following snippet to the JS file after the <code>items</code> object;\nnotice how I have removed unnecessary Fetch options\nand replaced hardcoded values with template strings:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> items <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token punctuation\">;</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> <span class=\"token punctuation\">[</span>date<span class=\"token punctuation\">,</span> rows<span class=\"token punctuation\">]</span> <span class=\"token keyword\">of</span> Object<span class=\"token punctuation\">.</span><span class=\"token function\">entries</span><span class=\"token punctuation\">(</span>items<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    date <span class=\"token operator\">=</span> date<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">' '</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">// E.g. \"Mon 2023-10-23\" -> \"2023-10-23\"</span>\n\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> <span class=\"token punctuation\">[</span>ticketId<span class=\"token punctuation\">,</span> time<span class=\"token punctuation\">]</span> <span class=\"token keyword\">of</span> rows<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      ticketId <span class=\"token operator\">??=</span> <span class=\"token string\">'FOO-111'</span> <span class=\"token comment\">// \"General stuff\" ticket</span>\n      time <span class=\"token operator\">*=</span> <span class=\"token number\">60</span> <span class=\"token comment\">// Hours to minutes</span>\n\n      <span class=\"token keyword\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>\n        <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">https://jira.example.com/rest/internal/3/issue/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>ticketId<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/worklog?adjustEstimate=new&amp;newEstimate=0m</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">body</span><span class=\"token operator\">:</span> <span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">stringify</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n            <span class=\"token literal-property property\">timeSpent</span><span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>time<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">m</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n            <span class=\"token literal-property property\">comment</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">'doc'</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">version</span><span class=\"token operator\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">content</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n            <span class=\"token literal-property property\">started</span><span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>date<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">T08:00:00.000+0200</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token literal-property property\">headers</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token literal-property property\">Accept</span><span class=\"token operator\">:</span> <span class=\"token string\">'application/json'</span><span class=\"token punctuation\">,</span>\n            <span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\">:</span> <span class=\"token string\">'application/json'</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          <span class=\"token literal-property property\">method</span><span class=\"token operator\">:</span> <span class=\"token string\">'POST'</span><span class=\"token punctuation\">,</span>\n          <span class=\"token literal-property property\">referrer</span><span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">https://jira.example.com/browse/</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>ticketId<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>There might still be some fluff (I don't know if e.g. the <code>referrer</code> is needed),\nbut good enough.</p>\n</li>\n</ol>\n<h4>Actually logging</h4>\n<ol>\n<li>\n<p>Open Jira in the browser.</p>\n</li>\n<li>\n<p>Copy-paste the code (including the <code>items</code> object) to the browser's JS console.</p>\n</li>\n<li>\n<p>Press <kbd>Enter</kbd> in the JS console\nand switch to the Network tab to monitor whether the Fetch requests succeed or fail.\n(So far they have never failed for me (<em>*knocks on wood*</em>).)</p>\n</li>\n<li>\n<p>Comment out the logged items in the JS file to avoid accidentally re-logging them the next time:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> items <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 'Mon 2023-11-06': [</span>\n  <span class=\"token comment\">//   ...</span>\n  <span class=\"token comment\">// ],</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>(Or just remove the logged items.)</p>\n</li>\n</ol>\n<p>Nice and easy!</p>\n","summary":"There's apparently no nice way to bulk-log hours in Jira.\nUse this One Weird Trick to do it anyway –\nJira consultants HATE it!","date_published":"2023-11-03T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/exact-dependency-versions/","url":"https://mtsknn.fi/blog/exact-dependency-versions/","title":"Pin exact dependency versions","content_html":"<p>In a perfect world,\nminor- and patch-level version bumps don't have breaking changes.\nIn the real world,\nthings are complicated and shit happens.</p>\n<p>Example: I recently witnessed\n<a href=\"/blog/yarn-install-failing/\" class=\"link\"><code>yarn install</code> starting to fail mysteriously after Node.js was automatically upgraded from v18.16 to v18.18</a>.</p>\n<p>I had thought that pinning the major-level version number of Node.js\nwould have prevented this kind of sudden breakages,\nbut nope.</p>\n<p>Had the Node.js version been pinned to v18.16.1 or even just v18.16,\n<code>yarn install</code> wouldn't have started to <em>suddenly</em> fail.</p>\n<p>Yes, <code>yarn install</code> would have probably started to fail later\nwhen upgrading Node.js <em>manually</em>,\nbut that's not as bad.\nSudden breakages are worse\nbecause they often require acute investigation,\nmaking your workdays more reactive than proactive.</p>\n<p>So, note to self:\npin exact dependency versions (<code>x.y.z</code> instead of just <code>x.y</code> or <code>x</code>)\nto be on the safe side.</p>\n","summary":"In a perfect world,\nminor- and patch-level version bumps don't have breaking changes.\nIn the real world,\nthings are complicated and shit happens.","date_published":"2023-11-02T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/app-insights-error-logging/","url":"https://mtsknn.fi/blog/app-insights-error-logging/","title":"`error.cause` needs to be logged manually to App Insights","content_html":"<p>If you are logging errors to Azure App Insights,\nand an Error object has a <code>cause</code> property (which too could be an Error object),\nthe <code>cause</code> Error's <code>message</code> and <code>stack</code> properties are not shown in Azure App Insights.\nBut you can log them manually.</p>\n<h2 id=\"problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problem\">Problem</a></h2>\n<ol>\n<li>\n<p>Use <a href=\"https://github.com/microsoft/ApplicationInsights-node.js\" class=\"link link-external\">App Insights SDK for Node.js</a>.</p>\n</li>\n<li>\n<p>Use the <code>trackException</code> method\nto log an error with a <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause\" class=\"link link-external\"><code>cause</code> property</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> <span class=\"token operator\">*</span> <span class=\"token keyword\">as</span> appInsights <span class=\"token keyword\">from</span> <span class=\"token string\">'applicationinsights'</span>\n\n<span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'foo'</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  appInsights<span class=\"token punctuation\">.</span>defaultClient<span class=\"token punctuation\">.</span><span class=\"token function\">trackException</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">exception</span><span class=\"token operator\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'bar'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">cause</span><span class=\"token operator\">:</span> error <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n</li>\n</ol>\n<p><strong>Expected behavior:</strong>\nboth Error's message and stack trace are shown in App Insights.</p>\n<p><strong>Actual behavior:</strong>\nonly the second Error's message (= <code>'bar'</code>) and stack trace are shown in App Insights.</p>\n<h3 id=\"a-less-contrived-example\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#a-less-contrived-example\">A less contrived example</a></h3>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">router<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/foo'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">req<span class=\"token punctuation\">,</span> res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> data <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> database<span class=\"token punctuation\">.</span><span class=\"token function\">getFoo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// This can throw</span>\n    res<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    appInsights<span class=\"token punctuation\">.</span>defaultClient<span class=\"token punctuation\">.</span><span class=\"token function\">trackException</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">exception</span><span class=\"token operator\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'GET /foo failed'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">cause</span><span class=\"token operator\">:</span> error <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h2 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h2>\n<p>Log the first/original Error's <code>stack</code> property manually:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  appInsights<span class=\"token punctuation\">.</span>defaultClient<span class=\"token punctuation\">.</span><span class=\"token function\">trackException</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">exception</span><span class=\"token operator\">:</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'...'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">properties</span><span class=\"token operator\">:</span>\n      error <span class=\"token keyword\">instanceof</span> <span class=\"token class-name\">Error</span> <span class=\"token operator\">?</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">causeStack</span><span class=\"token operator\">:</span> error<span class=\"token punctuation\">.</span>stack <span class=\"token punctuation\">}</span> <span class=\"token operator\">:</span> <span class=\"token keyword\">undefined</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Now the first/original Error's message and stack trace will be shown in App Insights\nunder the &quot;Custom Properties&quot; heading.</p>\n<h3 id=\"notes\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#notes\">Notes</a></h3>\n<ul>\n<li>\n<p>The <code>error instanceof Error</code> check is needed because:</p>\n<blockquote>\n<p>The value of <code>cause</code> can be of any type.\nYou should not make assumptions that the error you caught has an <code>Error</code> as its <code>cause</code>,\nin the same way that you cannot be sure the variable bound in the <code>catch</code> statement is an <code>Error</code> either.</p>\n</blockquote>\n<p>Source: <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause\" class=\"link link-external\"><em>Error: cause</em> on MDN</a></p>\n</li>\n<li>\n<p>No need to include <code>error.message</code> in the <code>properties</code> object,\nbecause <a href=\"/blog/js-error-stack/\" class=\"link\">in Node.js, <code>error.stack</code> also includes the error message</a>.</p>\n</li>\n</ul>\n","summary":"If you are logging errors to Azure App Insights,\nand an Error object has a `cause` property (which too could be an Error object),\nthe `cause` Error's `message` and `stack` properties are not shown in Azure App Insights.\nBut you can log them manually.","date_published":"2023-11-01T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/regex-numerals/","url":"https://mtsknn.fi/blog/regex-numerals/","title":"`\\d` in a regex isn't always equal to `[0-9]`","content_html":"<p>In JavaScript, <code>\\d</code> and <code>[0-9]</code> are equal and match only Arabic numerals (the numbers 0–9).\nBut in some other languages, <code>\\d</code> matches also non-Arabic numerals.</p>\n<p>There are <a href=\"https://www.fileformat.info/info/unicode/category/Nd/list.htm\" class=\"link link-external\">680 Unicode characters in the &quot;Number, Decimal Digit&quot; category</a>.\nFor example:</p>\n<table>\n<thead>\n<tr>\n<th>Name</th>\n<th>Characters</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Digits (<a href=\"https://en.wikipedia.org/wiki/Arabic_numerals\" class=\"link link-external\">Arabic numerals</a>)</td>\n<td>0123456789</td>\n</tr>\n<tr>\n<td>Arabic-Indic digits</td>\n<td>٠١٢٣٤٥٦٧٨٩</td>\n</tr>\n<tr>\n<td>Extended Arabic-Indic digits</td>\n<td>۰۱۲۳۴۵۶۷۸۹</td>\n</tr>\n<tr>\n<td>NKo digits</td>\n<td>߀߁߂߃߄߅߆߇߈߉</td>\n</tr>\n<tr>\n<td>Devanagari digits</td>\n<td>०१२३४५६७८९</td>\n</tr>\n</tbody>\n</table>\n<p>Testing on <a href=\"https://regex101.com/\" class=\"link link-external\">regex101.com</a>:</p>\n<ul>\n<li><a href=\"https://regex101.com/r/64mAxn/1\" class=\"link link-external\"><code>\\d</code> in JavaScript matches 10 of them</a> (Arabic numerals only)</li>\n<li><a href=\"https://regex101.com/r/64mAxn/2\" class=\"link link-external\"><code>\\d</code> in C# matches 370 of them</a></li>\n<li><a href=\"https://regex101.com/r/64mAxn/3\" class=\"link link-external\"><code>\\d</code> in Python matches 540 of them</a></li>\n<li><a href=\"https://regex101.com/r/64mAxn/4\" class=\"link link-external\"><code>\\d</code> in Rust matches all 680</a></li>\n</ul>\n<p>(I haven't tested whether <code>\\d</code> in those three languages matches also other characters than those 680.)</p>\n<p><code>[0-9]</code> matches only Arabic numerals in those four languages,\nbut <a href=\"https://unix.stackexchange.com/a/414230\" class=\"link link-external\">even <code>[0-9]</code> can't be always trusted</a>\n(a few typos fixed in the quotation):</p>\n<blockquote>\n<p>It is generally believed that <code>[0-9]</code> matches only the ASCII digits <code>0123456789</code>.\nThat is painfully false in some instances:\nLinux in some locale that is not &quot;C&quot; (June 2020) systems, for example:</p>\n<p>Assume:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token assign-left variable\">str</span><span class=\"token operator\">=</span><span class=\"token string\">'0123456789 ٠١٢٣٤٥٦٧٨٩ ۰۱۲۳۴۵۶۷۸۹ ߀߁߂߃߄߅߆߇߈߉ ०१२३४५६७८९'</span>\n</span></code></pre>\n<p>Try <code>grep</code> to discover that it allows most of them:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">$ <span class=\"token builtin class-name\">echo</span> <span class=\"token string\">\"<span class=\"token variable\">$str</span>\"</span> <span class=\"token operator\">|</span> <span class=\"token function\">grep</span> -o <span class=\"token string\">'[0-9]\\+'</span>\n0123456789\n٠١٢٣٤٥٦٧٨\n۰۱۲۳۴۵۶۷۸\n߀߁߂߃߄߅߆߇߈\n०१२३४५६७८\n</span></code></pre>\n<p><code>sed</code> has some troubles.\nShould remove only <code>0123456789</code> but removes almost all digits.\nThat means that it accepts most digits but not some nines (???):</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">$ <span class=\"token builtin class-name\">echo</span> <span class=\"token string\">\"<span class=\"token variable\">$str</span>\"</span> <span class=\"token operator\">|</span> <span class=\"token function\">sed</span> <span class=\"token string\">'s/[0-9]\\{1,\\}//g'</span>\n ٩ ۹ ߉ ९\n</span></code></pre>\n<p>Even <code>expr</code> suffers from the same issues as <code>sed</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">expr</span> <span class=\"token string\">\"<span class=\"token variable\">$str</span>\"</span> <span class=\"token builtin class-name\">:</span> <span class=\"token string\">'\\([0-9 ]*\\)'</span> <span class=\"token comment\"># also matching spaces</span>\n0123456789 ٠١٢٣٤٥٦٧٨\n</span></code></pre>\n<p>And also <code>ed</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token builtin class-name\">printf</span> <span class=\"token string\">'%s\\n'</span> <span class=\"token string\">'s/[0-9]/x/g'</span> <span class=\"token string\">'1,p'</span> Q <span class=\"token operator\">|</span> ed -v <span class=\"token operator\">&lt;</span><span class=\"token punctuation\">(</span><span class=\"token builtin class-name\">echo</span> <span class=\"token string\">\"<span class=\"token variable\">$str</span>\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token number\">105</span>\nxxxxxxxxxx xxxxxxxxx٩ xxxxxxxxx۹ xxxxxxxxx߉ xxxxxxxxx९\n</span></code></pre>\n</blockquote>\n<p>Huh. Curious.</p>\n<p>I guess I'd better remember to avoid <code>\\d</code> and prefer <code>[0-9]</code> or even <code>[0123456789]</code>\nwhen using other languages than JavaScript.</p>\n<p>When writing JavaScript,\nI'll continue using <code>\\d</code> as it's as clear (d = digit) as and shorter than <code>[0-9]</code>.</p>\n","summary":"In JavaScript, `\\d` and `[0-9]` are equal and match only Arabic numerals (the numbers 0–9).\nBut in some other languages, `\\d` matches also non-Arabic numerals.","date_published":"2023-10-31T12:00:00+03:00","tags":["JavaScript","Regular expressions"]},{"id":"https://mtsknn.fi/blog/response-json-can-throw/","url":"https://mtsknn.fi/blog/response-json-can-throw/","title":"`response.json()` can throw an error in JavaScript","content_html":"<p>If the response body is not valid JSON,\n<code>response.json()</code> will throw,\njust like <code>JSON.parse()</code> would throw\nif the same data was passed to it.</p>\n<p>Example:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Response</span><span class=\"token punctuation\">(</span><span class=\"token string\">'{ \"foo\": true }'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> { foo: true }</span>\n\n<span class=\"token comment\">// vs</span>\n\n<span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Response</span><span class=\"token punctuation\">(</span><span class=\"token string\">'OK'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data</span>\n</span></code></pre>\n<p>Quite obvious in hindsight,\nbut I was wondering this\nand couldn't find an answer quickly\n(I tried googling &quot;can response.json throw in js&quot;).</p>\n<p>How about <code>response.text()</code> – can it throw?\nNo, because it's akin to passing the same data to the <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/String\" class=\"link link-external\"><code>String()</code> function</a>,\nwhich <a href=\"https://tc39.es/ecma262/multipage/text-processing.html#sec-string-constructor\" class=\"link link-external\">doesn't seem to throw according to the language specs</a>.\n(Compare with <a href=\"https://tc39.es/ecma262/multipage/structured-data.html#sec-json.parse\" class=\"link link-external\">the language specs of <code>JSON.parse()</code></a>;\nthrowing is mentioned in step 2.)</p>\n","summary":"If the response body is not valid JSON,\n`response.json()` will throw,\njust like `JSON.parse()` would throw\nif the same data was passed to it.","date_published":"2023-10-30T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/js-error-stack/","url":"https://mtsknn.fi/blog/js-error-stack/","title":"`error.stack` includes the error message in some JS environments","content_html":"<p>In V8-based JS environments (e.g. Chrome, Deno and Node.js) and in Bun,\n<code>error.stack</code> = error class name + <code>error.message</code> + stack frames.</p>\n<h2 id=\"context\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#context\">Context</a></h2>\n<p>I was logging an Error object's <code>message</code> and <code>stack</code> properties to Azure App Insights\n(because <a href=\"/blog/app-insights-error-logging/\" class=\"link\"><code>error.cause</code> needs to be logged manually to App Insights</a>),\nand was baffled because <code>error.stack</code> seemed to contain not only the stack trace,\nbut also the error message.\nI thought App Insights was doing something wonky to the Error object.</p>\n<h2 id=\"culprit-v8\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#culprit-v8\">Culprit: V8</a></h2>\n<p>Eventually I realized that that's how the <a href=\"https://v8.dev/\" class=\"link link-external\">V8 JS engine</a> works.\nFrom <a href=\"https://v8.dev/docs/stack-trace-api\" class=\"link link-external\">V8's <em>Stack trace API</em> docs</a>:</p>\n<blockquote>\n<p>Here's an example of a fully formatted stack trace:</p>\n<pre aria-label=\"Code block\" class=\"language-plain bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">ReferenceError: FAIL is not defined\n   at Constraint.execute (deltablue.js:525:2)\n   at Constraint.recalculate (deltablue.js:424:21)\n   at Planner.addPropagate (deltablue.js:701:6)\n   at Constraint.satisfy (deltablue.js:184:15)\n   at Planner.incrementalAdd (deltablue.js:591:21)\n   at Constraint.addConstraint (deltablue.js:162:10)\n   at Constraint.BinaryConstraint (deltablue.js:346:7)\n   at Constraint.EqualityConstraint (deltablue.js:515:38)\n   at chainTest (deltablue.js:807:6)\n   at deltaBlue (deltablue.js:879:2)\n</span></code></pre>\n</blockquote>\n<p><a href=\"https://nodejs.org/api/errors.html#errorstack\" class=\"link link-external\">Node.js's <code>error.stack</code> docs</a>\nsay it more explicitly:</p>\n<blockquote>\n<p>The first line is formatted as <code>&lt;error class name&gt;: &lt;error message&gt;</code>,\nand is followed by a series of stack frames (each line beginning with &quot;at &quot;).</p>\n</blockquote>\n<p>(It's mentioned in <a href=\"https://nodejs.org/docs/latest-v4.x/api/errors.html#errors_error_stack\" class=\"link link-external\">Node.js v4 docs</a> as well.\n<a href=\"https://nodejs.org/en/blog/release/v4.0.0\" class=\"link link-external\">Node.js v4 was released in 2015.</a>)</p>\n<h2 id=\"non-v8-environments\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#non-v8-environments\">Non-V8 environments</a></h2>\n<p>In Firefox and Safari,\n<code>error.stack</code> contains only the stack trace.\nFirefox's JS engine is <a href=\"https://firefox-source-docs.mozilla.org/js/index.html\" class=\"link link-external\">SpiderMonkey</a>;\nSafari's JS engine is <a href=\"https://trac.webkit.org/wiki/JavaScriptCore\" class=\"link link-external\">JavaScriptCore</a>.</p>\n<p><a href=\"https://bun.sh/\" class=\"link link-external\">Bun</a> is interesting\nbecause it's also <a href=\"https://bun.sh/docs\" class=\"link link-external\">&quot;powered by JavaScriptCore&quot;</a>,\nbut its <code>error.stack</code> includes the error class name and error message as well.</p>\n<h2 id=\"non-standard\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#non-standard\">Non-standard</a></h2>\n<p><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/stack\" class=\"link link-external\">The <em><code>Error.prototype.stack</code></em> page on MDN</a>\nsays that it's a non-standard property:</p>\n<blockquote>\n<p><strong>Non-standard:</strong> This feature is non-standard and is not on a standards track.\nDo not use it on production sites facing the Web: it will not work for every user.\nThere may also be large incompatibilities between implementations and the behavior may change in the future.</p>\n</blockquote>\n<p>Using <code>error.stack</code> on the server is likely a-okay though\n(as long as you are familiar with how it works in your JS environment).</p>\n","summary":"In V8-based JS environments (e.g. Chrome, Deno and Node.js) and in Bun,\n`error.stack` = error class name + `error.message` + stack frames.","date_published":"2023-10-29T12:00:00+03:00","date_modified":"2023-11-02T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/yarn-install-failing/","url":"https://mtsknn.fi/blog/yarn-install-failing/","title":"`yarn install` failing mysteriously on Node.js v18.18","content_html":"<p>TL;DR: Upgrading Yarn from v1 to v4 fixed it for me.</p>\n<h2 id=\"problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problem\">Problem</a></h2>\n<p>Recently some CI pipelines<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup> started to fail at <code>yarn install</code> (v1.22.19)\nwith cryptic error messages like these (the error message kept changing):</p>\n<ul>\n<li>\n<pre aria-label=\"Code block\" class=\"language-plain bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">An unexpected error occurred:\n\"/path/to/project/node_modules/core-js-pure:\nCannot read properties of undefined (reading 'getOption')\".\n</span></code></pre>\n</li>\n<li>\n<pre aria-label=\"Code block\" class=\"language-plain bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">An unexpected error occurred:\n\"/path/to/project/node_modules/nodemon:\nCannot read properties of undefined (reading 'getOption')\".\n</span></code></pre>\n</li>\n<li>\n<pre aria-label=\"Code block\" class=\"language-plain bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">An unexpected error occurred:\n\"/path/to/project/node_modules/spawn-sync:\nCannot read properties of undefined (reading 'config')\".\n</span></code></pre>\n</li>\n</ul>\n<p>Googling those error messages didn't yield anything useful.</p>\n<h2 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h2>\n<p>The errors seemed to began after Node.js was upgraded from v18.16 to v18.18.</p>\n<p>Googling &quot;yarn install fails on node.js 18.18.2&quot; (:D)\nled me to <a href=\"https://github.com/yarnpkg/yarn/issues/3728#issuecomment-1737249792\" class=\"link link-external\">this GitHub comment on yarnpkg/yarn#3728</a>:</p>\n<blockquote>\n<p>6 years later, NPM removes the node-gyp binary from its bundle with v9.7.2 which is released with Node 18.18.\nBreaking our builds because now yarn tries to install <code>node-gyp</code> automatically and runs every now and then into this issue.\nTime to move away from yarn v1. :)</p>\n<p><a href=\"https://github.com/npm/cli/pull/6554#issuecomment-1726429283\" class=\"link link-external\">npm/cli#6554 (comment)</a></p>\n<p>Leaving a comment primarily for others running into this problem now.</p>\n</blockquote>\n<p><a href=\"https://yarnpkg.com/migration/guide\" class=\"link link-external\">Upgrading Yarn</a>\nto <a href=\"https://yarnpkg.com/blog/release/4.0\" class=\"link link-external\">Yarn v4, which was released just a few days ago</a>,\ndid indeed fix the <code>yarn install</code> errors.</p>\n<h3 id=\"side-comments\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#side-comments\">Side comments</a></h3>\n<ul>\n<li>Upgrading to Yarn v2 or v3 might have worked as well (I didn't test).</li>\n<li>I wasn't even aware that there are multiple versions of Yarn.\nMaybe because it's not a &quot;normal&quot; dependency.</li>\n<li><code>yarn install</code> on Yarn v1 typically took about ~1.5 minutes in the CI pipelines,\nwhereas on Yarn v4 it takes only about ~1 minute.\nA nice improvement!</li>\n</ul>\n<h2 id=\"the-real-lesson\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-real-lesson\">The real lesson</a></h2>\n<p>The real lesson here is to\n<a href=\"/blog/exact-dependency-versions/\" class=\"link\">pin exact dependency version numbers</a>.</p>\n<p>In my case Node.js was automatically upgraded from v18.16 to v18.18\nbecause the version of Node.js was pinned to v18.</p>\n<p>Had the Node.js version been pinned to v18.16.1 or v18.16 instead of just v18,\nthese errors wouldn't have started suddenly.</p>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>The CI pipelines were on Azure DevOps.\nThat's likely irrelevant,\nbut I still wanted to mention it, because:</p>\n<p>When investigating this problem,\nat first I didn't know what's causing it.\nOne of the Google searches I made was\n&quot;azure devops yarn install Cannot read properties of undefined (reading 'config&quot; (:D).</p>\n<p>That search didn't yield any useful results.\nThis page you are reading now would have been quite useful. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"TL;DR: Upgrading Yarn from v1 to v4 fixed it for me.","date_published":"2023-10-28T12:00:00+03:00","date_modified":"2023-11-02T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/visual-mnemonic-for-array-sorting/","url":"https://mtsknn.fi/blog/visual-mnemonic-for-array-sorting/","title":"Visual mnemonic for an array sorter's return values","content_html":"<p>When sorting an array with a custom comparator function,\nthe function should return <code>&lt;0</code>, <code>0</code> or <code>&gt;0</code>.\nBut what do the values do?\nUse this weird visual mnemonic –\nsorting consultants HATE it!</p>\n<p>First,\nrecall that an array sorter compares two values, <code>a</code> and <code>b</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">array<span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Second,\nbecause the sorter function should return a number,\nimagine an x-axis with the numbers <code>-1</code>, <code>0</code> and <code>+1</code>:</p>\n<p><img src=\"./x-axis.png\" alt=\"An x-axis with -1, 0 and +1.\"></p>\n<p>Now,\nrealize that <code>-1</code> is on the left side of the x-axis,\nand <code>+1</code> is on the right side:</p>\n<ul>\n<li>Returning <code>-1</code> (or anything less than zero) from the array sorter\nmoves <code>a</code> to the left (before <code>b</code> in the array).</li>\n<li>Returning <code>+1</code> (or anything greater than zero) from the array sorter\nmoves <code>a</code> to the right (after <code>b</code> in the array).</li>\n<li>(Returning <code>0</code> keeps the order of <code>a</code> and <code>b</code>.)</li>\n</ul>\n<p>And why is <code>a</code> moved relative to <code>b</code> –\nbecause the array sorter compares two values (<code>a</code> and <code>b</code>),\nand <code>a</code> comes first in the function parameters (<code>(a, b) =&gt; {}</code>).\nFirst come, first served.</p>\n<p>Like so:</p>\n<p><img src=\"./recap.png\" alt=\"An x-axis visualizing that when comparing values  and , returning -1 moves  to the left, and returning +1 moves  to the right.\"></p>\n<p>That's it!\nTo recap:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">array<span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span> <span class=\"token comment\">// &lt;-a</span>\n  <span class=\"token keyword\">return</span> <span class=\"token operator\">+</span><span class=\"token number\">1</span> <span class=\"token comment\">//   a-></span>\n  <span class=\"token keyword\">return</span> <span class=\"token number\">0</span> <span class=\"token comment\">// Keep order of `a` and `b`</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>(Illustrations mouse-drawn on <a href=\"https://www.tldraw.com/\" class=\"link link-external\">tldraw.com</a>.)</p>\n","summary":"When sorting an array with a custom comparator function,\nthe function should return `<0`, `0` or `>0`.\nBut what do the values do?\nUse this weird visual mnemonic –\nsorting consultants HATE it!","date_published":"2023-10-17T12:00:00+03:00","tags":["JavaScript","Mnemonics"]},{"id":"https://mtsknn.fi/blog/zod-object-strict/","url":"https://mtsknn.fi/blog/zod-object-strict/","title":"Strict object validation is handy for data exploration","content_html":"<p>Alternative title:\nI finally found a good use case for <code>z.object().strict()</code> in Zod.</p>\n<p>Zod's <code>z.object()</code> ignores unknown keys (= object keys not specified in a Zod schema) by default.\nUnknown keys can be disallowed with <a href=\"https://zod.dev/?id=strict\" class=\"link link-external\">Zod's <code>.strict()</code> method</a>.</p>\n<p>When would <code>.strict()</code> be useful?\nFrom <a href=\"https://github.com/colinhacks/zod/issues/2062#issuecomment-1455685434\" class=\"link link-external\">a GitHub comment by Kamil Trebunia</a>:</p>\n<blockquote>\n<p>For consuming APIs I would almost always pick <code>z.object</code>,\nbut for data exploration as I am doing now <code>z.strictObject</code> is necessary\nand I am glad I found about it.</p>\n</blockquote>\n<p>So: if you are exploring what data a poorly documented API returns,\nyou can use <code>z.object().strict()</code> (or the shorthand <code>z.strictObject()</code>)\nto avoid missing any keys;\nunspecified keys will make Zod throw an error,\nso you can adjust the Zod schema accordingly.</p>\n<p>There's no <code>.deepStrict()</code> method in Zod,\nbut it's easy to search for <code>.object(</code> and replace all occurrences with <code>.strictObject(</code>.</p>\n","summary":"Alternative title:\nI finally found a good use case for `z.object().strict()` in Zod.","date_published":"2023-10-10T12:00:00+03:00","tags":["JavaScript","TypeScript","Zod"]},{"id":"https://mtsknn.fi/blog/type-safer-node-cache/","url":"https://mtsknn.fi/blog/type-safer-node-cache/","title":"Adding type-safety to Node-Cache","content_html":"<p>Node-Cache (a JS library) has generic <code>get</code> and <code>set</code> methods,\nbut it's easy to accidentally misuse them.\nI made a type-safer wrapper around the library.</p>\n<h2 id=\"problem-context\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problem-context\">Problem / Context</a></h2>\n<p><a href=\"https://github.com/node-cache/node-cache\" class=\"link link-external\">Node-Cache is a caching module for Node.js</a>:</p>\n<blockquote>\n<p>A simple caching module that has <code>set</code>, <code>get</code> and <code>delete</code> methods\nand works a little bit like memcached.\nKeys can have a timeout (<code>ttl</code>) after which they expire and are deleted from the cache.\nAll keys are stored in a single object so the practical limit is at around 1m keys.</p>\n</blockquote>\n<p>Node-Cache's <code>get</code> and <code>set</code> methods are generic, which is nice:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> NodeCache <span class=\"token keyword\">from</span> <span class=\"token string\">'node-cache'</span>\n\n<span class=\"token keyword\">const</span> myCache <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">NodeCache</span><span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">)</span>\n\nmyCache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">set</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>Foo<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>cacheKey<span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> foo <span class=\"token operator\">=</span> myCache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">get</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>Foo<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>cacheKey<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>But the constructor is <em>not</em> generic,\nwhich is problematic;\nyou can accidentally use wrong types when getting and setting values.</p>\n<h3 id=\"example-case\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-case\">Example case</a></h3>\n<p>I was investigating a weird bug and was running out of ideas.\nFinally I noticed that a single cache was accidentally being used for two different things:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> NodeCache <span class=\"token keyword\">from</span> <span class=\"token string\">'node-cache'</span>\n\n<span class=\"token keyword\">const</span> fooCache <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">NodeCache</span><span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> barCache <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">NodeCache</span><span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">function</span> <span class=\"token function\">getFoo</span><span class=\"token punctuation\">(</span>id<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>fooCache<span class=\"token punctuation\">.</span><span class=\"token function\">has</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> fooCache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">get</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>Foo<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">const</span> value <span class=\"token operator\">=</span> <span class=\"token function\">getFooFromSomewhere</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  fooCache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">set</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>Foo<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> value\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">function</span> <span class=\"token function\">getBar</span><span class=\"token punctuation\">(</span>id<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>barCache<span class=\"token punctuation\">.</span><span class=\"token function\">has</span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> barCache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">get</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>Bar<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">const</span> value <span class=\"token operator\">=</span> <span class=\"token function\">getBarFromSomewhere</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token comment\">// ⚠️ Whoops! Accidentally using the wrong cache here</span>\n  fooCache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">set</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>Bar<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>id<span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> value\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h2 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h2>\n<h3 id=\"goal\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#goal\">Goal</a></h3>\n<p>Ideally the constructor would be generic,\nso that a cache can contain only certain types:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> fooCache <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">NodeCache<span class=\"token operator\">&lt;</span>Foo<span class=\"token operator\">></span></span><span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> foo <span class=\"token operator\">=</span> fooCache<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span>cacheKey<span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> Foo | undefined</span>\nfooCache<span class=\"token punctuation\">.</span><span class=\"token function\">set</span><span class=\"token punctuation\">(</span>cacheKey<span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">)</span> <span class=\"token comment\">// `value` must be of type `Foo`</span>\n</span></code></pre>\n<p>Currently this is not possible;\n<a href=\"https://github.com/node-cache/node-cache/issues/273\" class=\"link link-external\">GitHub issue #273</a>\nis asking for this.</p>\n<p>The issue was created in December 2021,\nand the latest commit to the project was made in November 2021,\nso the issue might never get solved.</p>\n<p>I solved it myself by creating a type-safer wrapper around Node-Cache.</p>\n<h3 id=\"code\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#code\">Code</a></h3>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// eslint-disable-next-line no-restricted-imports</span>\n<span class=\"token keyword\">import</span> NodeCache<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> Key<span class=\"token punctuation\">,</span> Options<span class=\"token punctuation\">,</span> ValueSetItem <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'node-cache'</span>\n\n<span class=\"token comment\">/**\n * A type-safer wrapper around [Node-Cache](https://github.com/node-cache/node-cache).\n *\n * Via https://mtsknn.fi/blog/type-safer-node-cache/\n *\n * @example\n * const myCache = Cache&lt;MyType>(options)\n * myCache.set('foo', value)\n * myCache.get('foo')\n *\n * @example\n * // Error: Must provide a type parameter\n * const myCache = Cache(options)\n */</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">function</span> <span class=\"token generic-function\"><span class=\"token function\">Cache</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">void</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>\n  <span class=\"token comment\">// A fancy way to make the `T` parameter required:</span>\n  <span class=\"token comment\">// https://stackoverflow.com/a/51173322/1079869</span>\n  options<span class=\"token operator\">:</span> Options <span class=\"token operator\">&amp;</span>\n    <span class=\"token punctuation\">(</span><span class=\"token constant\">T</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\"><span class=\"token keyword\">void</span></span>\n      <span class=\"token operator\">?</span> <span class=\"token string\">'Error: Must provide a type parameter (`Cache({})` -> `Cache&lt;T>({})`)'</span>\n      <span class=\"token operator\">:</span> Options<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">NodeCache</span><span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// Type casting required to not lose some methods (e.g. `del`)</span>\n    <span class=\"token operator\">...</span><span class=\"token punctuation\">(</span>cache <span class=\"token keyword\">as</span> Required<span class=\"token operator\">&lt;</span><span class=\"token keyword\">typeof</span> cache<span class=\"token operator\">></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n\n    <span class=\"token comment\">// Only the generic methods of Node-Cache are overridden</span>\n    <span class=\"token function-variable function\">get</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>key<span class=\"token operator\">:</span> Key<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> cache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">get</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function-variable function\">mget</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>keys<span class=\"token operator\">:</span> Key<span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> cache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">mget</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>keys<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function-variable function\">mset</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>keyValueSet<span class=\"token operator\">:</span> ValueSetItem<span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> cache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">mset</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>keyValueSet<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function-variable function\">set</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>key<span class=\"token operator\">:</span> Key<span class=\"token punctuation\">,</span> value<span class=\"token operator\">:</span> <span class=\"token constant\">T</span><span class=\"token punctuation\">,</span> ttl<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token operator\">|</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n      cache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">set</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">,</span> ttl <span class=\"token keyword\">as</span> <span class=\"token builtin\">number</span> <span class=\"token operator\">|</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token function-variable function\">take</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>key<span class=\"token operator\">:</span> Key<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> cache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">take</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h4>Notes</h4>\n<ul>\n<li>\n<p>Apparently there's no built-in way to mark generic type parameters as required.\nSo instead of using <code>&lt;T&gt;</code> and <code>options: Options</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">function</span> <span class=\"token generic-function\"><span class=\"token function\">Cache</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>options<span class=\"token operator\">:</span> Options<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>...I'm using\n<a href=\"https://stackoverflow.com/a/51173322/1079869\" class=\"link link-external\">a fancy way to make the generic <code>T</code> parameter required</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">function</span> <span class=\"token generic-function\"><span class=\"token function\">Cache</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">void</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>\n  options<span class=\"token operator\">:</span> Options <span class=\"token operator\">&amp;</span>\n    <span class=\"token punctuation\">(</span><span class=\"token constant\">T</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\"><span class=\"token keyword\">void</span></span>\n      <span class=\"token operator\">?</span> <span class=\"token string\">'Error: Must provide a type parameter (`Cache({})` -> `Cache&lt;T>({})`)'</span>\n      <span class=\"token operator\">:</span> Options<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token function\">Cache</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//    ^? Argument of type '{}'</span>\n<span class=\"token comment\">//       is not assignable to parameter of type</span>\n<span class=\"token comment\">//       'Options &amp; \"Error: Must provide a type parameter (`Cache({})` -> `Cache&lt;T>({})`)\"'.</span>\n\n<span class=\"token generic-function\"><span class=\"token function\">Cache</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>Foo<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK</span>\n</span></code></pre>\n<p>Unfortunately this also makes the <code>options</code> parameter required.\nPassing an empty object is OK.</p>\n<p>Not ideal, but better than nothing.</p>\n</li>\n<li>\n<p>For some reason the spread syntax causes some methods to be lost:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">function</span> <span class=\"token generic-function\"><span class=\"token function\">Cache</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>options<span class=\"token operator\">:</span> Options<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">NodeCache</span><span class=\"token punctuation\">(</span>options<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">...</span>cache<span class=\"token punctuation\">,</span>\n    <span class=\"token function-variable function\">get</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>key<span class=\"token operator\">:</span> Key<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> cache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">get</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">// ...</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> fooCache <span class=\"token operator\">=</span> <span class=\"token generic-function\"><span class=\"token function\">Cache</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>Foo<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\nfooCache<span class=\"token punctuation\">.</span><span class=\"token function\">del</span><span class=\"token punctuation\">(</span><span class=\"token string\">'bar'</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//       ^? Property 'del' does not exist on type '...'.</span>\n</span></code></pre>\n<p>Type casting fixes the problem (I don't know why):</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token operator\">...</span><span class=\"token punctuation\">(</span>cache <span class=\"token keyword\">as</span> Required<span class=\"token operator\">&lt;</span><span class=\"token keyword\">typeof</span> cache<span class=\"token operator\">></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function-variable function\">get</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>key<span class=\"token operator\">:</span> Key<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> cache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">get</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>At first I used <code>...(cache as Omit&lt;typeof cache, 'get' | 'mget' | 'mset' | 'set' | 'take'&gt;)</code>,\nbut <code>...(cache as Required&lt;typeof cache&gt;)</code>\nand <code>...(cache as Readonly&lt;typeof cache&gt;)</code>\nseem to work as well.</p>\n</li>\n<li>\n<p>Only the generic methods of Node-Cache need to be overridden;\nthere's nothing to fix in the non-generic methods (unless I'm missing something).</p>\n</li>\n<li>\n<p>You might want to copy the JSDoc comments from Node-Cache's <code>index.d.ts</code>.\nFor example (comment slightly clarified by me):</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// ...</span>\n\n  <span class=\"token comment\">/**\n   * Get a cached key and remove it from the cache.\n   * Equivalent to calling `get(key)` + `del(key)`.\n   *\n   * Useful for implementing \"single use\" mechanism such as OTP (one-time password),\n   * where once a value is read it will become obsolete.\n   */</span>\n  <span class=\"token function-variable function\">take</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>key<span class=\"token operator\">:</span> Key<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> cache<span class=\"token punctuation\">.</span><span class=\"token generic-function\"><span class=\"token function\">take</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>key<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n</li>\n</ul>\n<h3 id=\"bonus-eslint\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#bonus-eslint\">Bonus: ESLint</a></h3>\n<p>One more step:\nuse ESLint to prevent importing Node-Cache\nso that everyone will use the type-safer wrapper instead.</p>\n<p>There are two built-in ESLint rules for this:</p>\n<ul>\n<li><a href=\"https://eslint.org/docs/latest/rules/no-restricted-imports\" class=\"link link-external\"><code>no-restricted-imports</code></a>\nto &quot;disallow specified modules when loaded by <code>import</code>.&quot;</li>\n<li><a href=\"https://eslint.org/docs/latest/rules/no-restricted-modules\" class=\"link link-external\"><code>no-restricted-modules</code></a>\nto &quot;disallow specified modules when loaded by <code>require</code>&quot;;\nuseful if you are still using CommonJS modules.</li>\n</ul>\n<p>Like so:</p>\n<pre aria-label=\"Code block\" class=\"language-json bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// .eslintc</span>\n\n<span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"rules\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token property\">\"no-restricted-imports\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token string\">\"error\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">{</span> <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"node-cache\"</span><span class=\"token punctuation\">,</span> <span class=\"token property\">\"message\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Import the `Cache` utility instead.\"</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"no-restricted-modules\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token string\">\"error\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">{</span> <span class=\"token property\">\"name\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"node-cache\"</span><span class=\"token punctuation\">,</span> <span class=\"token property\">\"message\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Import the `Cache` utility instead.\"</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Example:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// foo.ts</span>\n\n<span class=\"token keyword\">import</span> NodeCache <span class=\"token keyword\">from</span> <span class=\"token string\">'node-cache'</span>\n<span class=\"token comment\">//=> 'node-cache' import is restricted from being used.</span>\n<span class=\"token comment\">//   Import the `Cache` utility instead.</span>\n<span class=\"token comment\">//   eslint(no-restricted-imports)</span>\n</span></code></pre>\n","summary":"Node-Cache (a JS library) has generic `get` and `set` methods,\nbut it's easy to accidentally misuse them.\nI made a type-safer wrapper around the library.","date_published":"2023-08-22T12:00:00+03:00","date_modified":"2023-08-30T12:00:00+03:00","tags":["ESLint","JavaScript","TypeScript"]},{"id":"https://mtsknn.fi/blog/youtube-transcript-tool/","url":"https://mtsknn.fi/blog/youtube-transcript-tool/","title":"YouTube's \"Show transcript\" tool is great for taking video notes","content_html":"<p>Incidentally,\nit's also great for searching for certain words in a video.</p>\n<p>Click the three dots under a video and then &quot;Show transcript.&quot;\nExample screenshot from\n<a href=\"https://www.youtube.com/watch?v=AKTyPgwfPgg\" class=\"link link-external\"><em>Agile Product Ownership in a Nutshell</em> (15:51) by Henrik Kniberg</a>:</p>\n<p><img src=\"./screenshot.png\" alt=\"Annotated (1–2–3) screenshot of where to find YouTube's &quot;Show transcript&quot; tool and the transcript.\"></p>\n<p>This tool saves time and effort:\nI don't need to be a human speech-to-text machine;\nI can instead focus on fixing typos and errors in the transcript\nand splitting the text into several paragraphs and bullet points\nto make it easier to read.\nTwo examples:</p>\n<ul>\n<li><a href=\"/blog/agile-process-visualized/\" class=\"link\"><em>Video notes: Agile Product Ownership in a Nutshell</em></a></li>\n<li><a href=\"/blog/legacy-code/\" class=\"link\"><em>Legacy code is code written by other people</em></a></li>\n</ul>\n<p>Side note:\nI opened an incognito window to take the screenshot,\nand uhh... YouTube looks so bloated.\nProtip:\nblock the <code>#related</code> element (the &quot;related videos&quot; container on the right) with <a href=\"https://github.com/gorhill/uBlock\" class=\"link link-external\">uBlock Origin</a>\n(or use other tools to debloat YouTube).</p>\n","summary":"Incidentally,\nit's also great for searching for certain words in a video.","date_published":"2023-06-28T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/youtube-still-image-bookmarklet/","url":"https://mtsknn.fi/blog/youtube-still-image-bookmarklet/","title":"Bookmarklet to download still images from YouTube videos","content_html":"<p>Quick and dirty code to grab a single frame of a YouTube video.\nMight work with other video players too.</p>\n<h2 id=\"the-bookmarklet\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-bookmarklet\">The bookmarklet</a></h2>\n<p>Drag and drop this to the browser's bookmarks bar:</p>\n<p><a class=\"link\" href=\"javascript:(() => { const $video = document.querySelector('video'); const { height, width } = $video.getBoundingClientRect(); const $canvas = document.createElement('canvas'); const ctx = $canvas.getContext('2d'); ctx.canvas.height = height; ctx.canvas.width = width; ctx.drawImage($video, 0, 0, width, height); const $a = document.createElement('a'); $a.download = 'frame.png'; $a.href = $canvas.toDataURL(); $a.click(); })()\">YouTube: Download still image</a></p>\n<h3 id=\"usage\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#usage\">Usage</a></h3>\n<ol>\n<li>Open a YouTube video</li>\n<li>Maximize the browser window and use YouTube's &quot;Theater mode&quot; for a larger result</li>\n<li>Pause the video at some point</li>\n<li>Run the bookmarklet; it will download a PNG image</li>\n</ol>\n<h3 id=\"code\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#code\">Code</a></h3>\n<h4>Minified</h4>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token literal-property property\">javascript</span><span class=\"token operator\">:</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span> <span class=\"token keyword\">const</span> $video <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">querySelector</span><span class=\"token punctuation\">(</span><span class=\"token string\">'video'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> height<span class=\"token punctuation\">,</span> width <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> $video<span class=\"token punctuation\">.</span><span class=\"token function\">getBoundingClientRect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">const</span> $canvas <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'canvas'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">const</span> ctx <span class=\"token operator\">=</span> $canvas<span class=\"token punctuation\">.</span><span class=\"token function\">getContext</span><span class=\"token punctuation\">(</span><span class=\"token string\">'2d'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> ctx<span class=\"token punctuation\">.</span>canvas<span class=\"token punctuation\">.</span>height <span class=\"token operator\">=</span> height<span class=\"token punctuation\">;</span> ctx<span class=\"token punctuation\">.</span>canvas<span class=\"token punctuation\">.</span>width <span class=\"token operator\">=</span> width<span class=\"token punctuation\">;</span> ctx<span class=\"token punctuation\">.</span><span class=\"token function\">drawImage</span><span class=\"token punctuation\">(</span>$video<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> width<span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">const</span> $a <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> $a<span class=\"token punctuation\">.</span>download <span class=\"token operator\">=</span> <span class=\"token string\">'frame.png'</span><span class=\"token punctuation\">;</span> $a<span class=\"token punctuation\">.</span>href <span class=\"token operator\">=</span> $canvas<span class=\"token punctuation\">.</span><span class=\"token function\">toDataURL</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> $a<span class=\"token punctuation\">.</span><span class=\"token function\">click</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h4>Unminified</h4>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token literal-property property\">javascript</span><span class=\"token operator\">:</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> $video <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">querySelector</span><span class=\"token punctuation\">(</span><span class=\"token string\">'video'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> height<span class=\"token punctuation\">,</span> width <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> $video<span class=\"token punctuation\">.</span><span class=\"token function\">getBoundingClientRect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> $canvas <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'canvas'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> ctx <span class=\"token operator\">=</span> $canvas<span class=\"token punctuation\">.</span><span class=\"token function\">getContext</span><span class=\"token punctuation\">(</span><span class=\"token string\">'2d'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  ctx<span class=\"token punctuation\">.</span>canvas<span class=\"token punctuation\">.</span>height <span class=\"token operator\">=</span> height<span class=\"token punctuation\">;</span>\n  ctx<span class=\"token punctuation\">.</span>canvas<span class=\"token punctuation\">.</span>width <span class=\"token operator\">=</span> width<span class=\"token punctuation\">;</span>\n  ctx<span class=\"token punctuation\">.</span><span class=\"token function\">drawImage</span><span class=\"token punctuation\">(</span>$video<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> width<span class=\"token punctuation\">,</span> height<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> $a <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  $a<span class=\"token punctuation\">.</span>download <span class=\"token operator\">=</span> <span class=\"token string\">'frame.png'</span><span class=\"token punctuation\">;</span>\n  $a<span class=\"token punctuation\">.</span>href <span class=\"token operator\">=</span> $canvas<span class=\"token punctuation\">.</span><span class=\"token function\">toDataURL</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  $a<span class=\"token punctuation\">.</span><span class=\"token function\">click</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h2 id=\"next-steps\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#next-steps\">Next steps</a></h2>\n<p>Your needs may be different from mine,\nbut here's what I usually do:</p>\n<ol>\n<li><a href=\"https://support.apple.com/guide/preview/resize-rotate-or-flip-an-image-prvw2015/mac\" class=\"link link-external\">Resize the image using Mac's Preview app</a></li>\n<li>Compress the image with <a href=\"https://tinypng.com/\" class=\"link link-external\">TinyPNG</a></li>\n<li>Use the image in a blog post</li>\n</ol>\n<p>The end result might have poor quality, but that's all right.\nI use this only for getting still images for video notes.</p>\n<p>For an example,\ncheck out yesterday's post,\n<a href=\"/blog/legacy-code/\" class=\"link\"><em>Legacy code is code written by other people</em></a>.\nLooks good enough for me.</p>\n<h2 id=\"acknowledgements\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#acknowledgements\">Acknowledgements</a></h2>\n<p>I googled for &quot;js video get frame screenshot.&quot;\nThe first result was <a href=\"https://stackoverflow.com/q/19175174/1079869\" class=\"link link-external\">&quot;<em>Capture frames from video with HTML5 and JavaScript</em>&quot; question on Stack Overflow</a>\nwhich gave me a good starting point.</p>\n","summary":"Quick and dirty code to grab a single frame of a YouTube video.\nMight work with other video players too.","date_published":"2023-06-27T12:00:00+03:00","tags":["Bookmarklets","JavaScript"]},{"id":"https://mtsknn.fi/blog/legacy-code/","url":"https://mtsknn.fi/blog/legacy-code/","title":"Legacy code is code written by other people","content_html":"<p>Legacy code means unfamiliar code.\nLegacy code has much more to do with unfamiliarity\nthan the quality of the code itself.</p>\n<p>From the <a href=\"https://www.youtube.com/watch?v=w9YhmMPLQ4U\" class=\"link link-external\"><em>Prioritizing Technical Debt as If Time &amp; Money Matters</em> talk by Adam Tornhill (28:42)</a>,\nfrom 19:53–22:14:</p>\n<blockquote>\n<p>Legacy code is related to technical debt,\nbut the two are not the same.</p>\n<p>So what is legacy code?</p>\n<p>There are many definitions out there.\nThe definition I prefer is that:</p>\n<ul>\n<li><strong>Legacy code is code that somehow lacks in quality,</strong></li>\n<li><strong>and more importantly, it's code that we didn't write ourselves.</strong></li>\n</ul>\n<p>Right.</p>\n<p>And that second part is really important,\nso let me share a story with you.\nThis is something that happened to me five years ago,\nand I've met it several times since then.</p>\n<p>What happened here was that I was visiting our client.\nI did a workshop for one of their teams.\nWe did a hotspot analysis of their code bases.\nAnd they had two code bases.\nSo we analyzed them.\nWe talked about them,\ndiscussed how we could address the findings.</p>\n<p>And then, after a while,</p>\n<ul>\n<li>someone in the team mentioned that &quot;You know what? We actually have a third code base as well.&quot;</li>\n<li>So I start to think &quot;Okay, should we analyze that one too?&quot;</li>\n<li>And everyone kind of looked at each other\nand laughed a little bit like &quot;Uh, no, we don't really have to do that.&quot;</li>\n<li>So I said &quot;Why not?&quot;</li>\n<li>&quot;Because we know the third code base is a mess.&quot;</li>\n<li>And I was like &quot;Yeah, we <em>really</em> have to look at it,&quot; right?</li>\n</ul>\n<p>So we did.\nAnd we looked at the hot spot[s],\nwe looked at the code health metrics,\n<a href=\"https://en.wikipedia.org/wiki/Cyclomatic_complexity\" class=\"link link-external\">cyclomatic complexity</a>,\nall that stuff...</p>\n<p>And guess what?\nObjectively there was absolutely no difference in code quality between the first two code bases and the third one.</p>\n<p>And you know, when you bring up something like that,\neveryone starts to question it,\nthat &quot;Hey, maybe the tool is measuring the wrong thing;\nmaybe you are measuring the wrong thing.&quot;\nRight?</p>\n<p>So we had to spend a lot of time actually comparing code samples,\nand after a while everyone was fairly convinced –\neven though they didn't like it –\nthat code base number three was indeed in no worse shape than the other two.</p>\n<p>So why did everyone think that it was such a mess?</p>\n<p>The reason of course was that\ncode base number three turned out to be developed in a different part of the organization,\nin a team that has since been disbanded.\nAnd this team had simply inherited that code base,\nmeaning they were now responsible for a piece of code\nthat they didn't write themselves.\nThey didn't understand the problem domain,\nand they didn't understand the solution.</p>\n<p><strong>So [legacy code] has much more to do with unfamiliarity\nthan any properties of the code itself.</strong></p>\n</blockquote>\n<p>Makes sense!\nLow-quality code can be okay to work with if it's familiar enough.\nOn the other hand,\nunfamiliar code can feel smelly and intimidating at first,\neven if it turns out to be of high quality.</p>\n<p>When someone leaves a team or an organization,\nthey leave behind legacy code\n(read: code that is unfamiliar to others),\nunless someone else happens to be familiar with all the current code authored by the leaving person.</p>\n<p><img src=\"./adam-tornhill.png\" alt=\"A snapshot from the video (21:15), with Adam Tornhill on the left and an illustration on the right labeled &quot;The Technical Debt That Wasn't&quot;\"></p>\n","summary":"Legacy code means unfamiliar code.\nLegacy code has much more to do with unfamiliarity\nthan the quality of the code itself.","date_published":"2023-06-26T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/date-objects-vs-date-strings/","url":"https://mtsknn.fi/blog/date-objects-vs-date-strings/","title":"`Date` objects are nicer to work with than date strings","content_html":"<p>When parsing data,\ndate strings should likely be converted to <code>Date</code> objects.\nBut beware mutations.</p>\n<h2 id=\"intended-audience-applicable-languages\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#intended-audience-applicable-languages\">Intended audience / Applicable languages</a></h2>\n<p>This post is for you if you are dealing with date strings.</p>\n<p>This post uses JavaScript and TypeScript,\nbut the main ideas are likely quite language-agnostic.</p>\n<h2 id=\"context\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#context\">Context</a></h2>\n<p>Let's say some data from e.g. an API or database contains a date string in ISO format:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> data <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  createdOn<span class=\"token operator\">:</span> <span class=\"token string\">'2023-06-25T01:23:45Z'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>When validating/parsing the data,\nthe date string could at the same time be transformed to a <code>Date</code> object.\n(See <a href=\"/blog/parse-dont-just-validate/\" class=\"link\"><em>Parsing data is nicer than only validating it</em></a>.)</p>\n<p>Example using <a href=\"https://zod.dev/\" class=\"link link-external\">Zod, a TypeScript validation library</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> z <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'zod'</span>\n\n<span class=\"token keyword\">const</span> Schema <span class=\"token operator\">=</span> z<span class=\"token punctuation\">.</span><span class=\"token function\">object</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  createdOn<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">datetime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">pipe</span><span class=\"token punctuation\">(</span>z<span class=\"token punctuation\">.</span>coerce<span class=\"token punctuation\">.</span><span class=\"token function\">date</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> Schema<span class=\"token punctuation\">.</span><span class=\"token function\">parse</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//    ^? { createdOn: Date; ... }</span>\n</span></code></pre>\n<h2 id=\"why-date-objects-are-better\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-date-objects-are-better\">Why <code>Date</code> objects are better</a></h2>\n<h3 id=\"semantically-correct\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#semantically-correct\">Semantically correct</a></h3>\n<p>A date string is not a date;\nit's a <a href=\"https://en.wikipedia.org/wiki/Serialization\" class=\"link link-external\">serialized</a> format of a date.</p>\n<p>When parsing data,\nit's semantically correct to convert/deserialize date strings to <code>Date</code> objects.</p>\n<p>(Not sure if &quot;semantically correct&quot; is the correct term here,\nbut anyway.)</p>\n<h3 id=\"convert-only-once\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#convert-only-once\">Convert only once</a></h3>\n<p>If you convert a date string to a <code>Date</code> object right after receiving the data,\nyou need to do the conversion only once.</p>\n<p>This avoids shotgun parsing,\nor repeating and scattering date parsing here and there.\n(More about shotgun parsing in <a href=\"/blog/parse-dont-just-validate/\" class=\"link\"><em>Parsing data is nicer than only validating it</em></a>.)</p>\n<h3 id=\"tolocalestring-et-al\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#tolocalestring-et-al\"><code>toLocaleString()</code> et al.</a></h3>\n<p>If you have a <code>Date</code> object,\nyou can directly use\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString\" class=\"link link-external\"><code>Date.prototype.toLocaleString()</code></a>\nand other <code>Date</code> methods.</p>\n<p>If you have a date string,\nyou first need to convert the string to a <code>Date</code> object –\nevery time you want to use <code>toLocaleString()</code> (shotgun parsing).</p>\n<p>Even if you have wrapper functions for formatting dates (probably a good thing),\nyou could make the functions take a <code>Date</code> argument\ninstead of a <code>Date | string</code> argument (simplicity).</p>\n<h3 id=\"date-fns-et-al\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#date-fns-et-al\">date-fns et al.</a></h3>\n<p><a href=\"https://date-fns.org/\" class=\"link link-external\">date-fns is a JavaScript date utility library.</a></p>\n<p>Most date-fns functions take <code>Date</code> arguments, not date string arguments,\nso you can do e.g. just <code>startOfWeek(date)</code>\ninstead of <code>startOfWeek(parseISO(dateString))</code> (shotgun parsing, again).</p>\n<h2 id=\"beware-mutations\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#beware-mutations\">Beware mutations</a></h2>\n<p>One drawback is that <code>Date</code> objects are mutable.\nAccidental mutations can lead to hard-to-track bugs;\ndeliberate mutations can lead to hard-to-reason code.</p>\n<p>This might be not such a big issue if the object is not otherwise guarded;\n<code>obj.createdOn.setFullYear(2000)</code> might not be much worse than any of these:</p>\n<ul>\n<li><code>obj.createdOn = '2000' + obj.createdOn.slice(4)</code></li>\n<li><code>obj.name = 'foo'</code></li>\n<li><code>obj.items.push('bar')</code></li>\n</ul>\n<p>Anyhow,\nthere are a few ways to avoid <code>Date</code> mutations\n(these are not specific to Zod):</p>\n<h3 id=\"use-a-function\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#use-a-function\">Use a function</a></h3>\n<p>Instead of <code>obj.createdOn</code>, make it a function <code>obj.getCreatedOn()</code> that creates a new <code>Date</code> object every time:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> obj <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function-variable function\">getCreatedOn</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span><span class=\"token string\">'2023-06-25T01:23:45Z'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> a <span class=\"token operator\">=</span> obj<span class=\"token punctuation\">.</span><span class=\"token function\">getCreatedOn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> b <span class=\"token operator\">=</span> obj<span class=\"token punctuation\">.</span><span class=\"token function\">getCreatedOn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\na <span class=\"token operator\">===</span> b <span class=\"token comment\">//=> false</span>\n</span></code></pre>\n<p><strong>Good:</strong>\nIf you mutate the <code>Date</code> object returned by <code>obj.getCreatedOn()</code>,\nthe mutation affects only the freshly-created <code>Date</code> object.\nThat's better than the mutation affecting who knows how many places.</p>\n<p><strong>Bad:</strong>\nSome object properties are now functions and some are not,\nmaking the object asymmetrical (<code>obj.name</code> vs <code>obj.getCreatedOn()</code>).</p>\n<h3 id=\"use-a-getter-property\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#use-a-getter-property\">Use a getter property</a></h3>\n<p>Very similar to above,\nbut make <code>obj.createdOn</code> a <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get\" class=\"link link-external\">getter property</a> instead:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> obj <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">get</span> <span class=\"token function\">createdOn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span><span class=\"token string\">'2023-06-25T01:23:45Z'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> a <span class=\"token operator\">=</span> obj<span class=\"token punctuation\">.</span>createdOn\n<span class=\"token keyword\">const</span> b <span class=\"token operator\">=</span> obj<span class=\"token punctuation\">.</span>createdOn\n\na <span class=\"token operator\">===</span> b <span class=\"token comment\">//=> false</span>\n</span></code></pre>\n<p><strong>Good:</strong>\nSame as above + now the object is more symmetrical (<code>obj.name</code> vs <code>obj.createdOn</code>).</p>\n<p><strong>Bad:</strong>\nI don't like getters and setters because they are not transparent;\nthey look like normal object properties, but they are not.</p>\n<h3 id=\"use-typescript\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#use-typescript\">Use TypeScript</a></h3>\n<p>TypeScript doesn't have a built-in type for read-only <code>Date</code>s,\nbut such a type can be easily created:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">ReadonlyDate</span> <span class=\"token operator\">=</span> Readonly<span class=\"token operator\">&lt;</span>\n  Omit<span class=\"token operator\">&lt;</span>\n    Date<span class=\"token punctuation\">,</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setDate'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setFullYear'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setHours'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setMilliseconds'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setMinutes'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setMonth'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setSeconds'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setTime'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setUTCDate'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setUTCFullYear'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setUTCHours'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setUTCMilliseconds'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setUTCMinutes'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setUTCMonth'</span>\n    <span class=\"token operator\">|</span> <span class=\"token string\">'setUTCSeconds'</span>\n  <span class=\"token operator\">></span>\n<span class=\"token operator\">></span>\n</span></code></pre>\n<p>(Credit: Daniel Nixon;\nthe type is from <a href=\"https://github.com/date-fns/date-fns/issues/1944\" class=\"link link-external\">date-fns GitHub issue <em>Suggestion: use a readonly Date type in TypeScript</em></a>.\nAlso, using <code>Readonly&lt;...&gt;</code> is probably unnecessary here.)</p>\n<p><strong>Good:</strong>\nLeverages the type system;\ndoesn't affect runtime.</p>\n<p><strong>Bad:</strong>\nE.g. date-fns functions don't accept <code>ReadonlyDate</code> arguments:</p>\n<blockquote>\n<p>Argument of type <code>ReadonlyDate</code> is not assignable to parameter of type <code>number | Date</code>.</p>\n<blockquote>\n<p>Type <code>ReadonlyDate</code> is missing the following properties from type <code>Date</code>: <code>setTime</code>, <code>setMilliseconds</code>, <code>setUTCMilliseconds</code>, <code>setSeconds</code>, and 11 more.</p>\n</blockquote>\n</blockquote>\n<h3 id=\"use-eslint\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#use-eslint\">Use ESLint</a></h3>\n<p><code>Date</code> mutations could be theoretically banned with ESLint.</p>\n<p><a href=\"https://www.npmjs.com/package/eslint-plugin-better-dates\" class=\"link link-external\"><code>eslint-plugin-better-dates</code></a> does this,\nbut the plugin looks outdated.\nIt would probably be quite easy to create a similar plugin from scratch.</p>\n<p><strong>Good:</strong>\nSounds like a job suitable for ESLint.</p>\n<p><strong>Bad:</strong>\nThe plugin looks outdated.</p>\n<p>Using ESLint might be my choice to avoid <code>Date</code> mutations.</p>\n<h2 id=\"summary\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#summary\">Summary</a></h2>\n<ul>\n<li>A date string is a serialized format of a date.</li>\n<li>When parsing data,\nit's &quot;correct&quot; to deserialize date strings to <code>Date</code> objects.</li>\n<li>Correctness aside, <code>Date</code> objects are also nicer to work with.</li>\n<li>Beware accidental mutations of <code>Date</code> objects.</li>\n</ul>\n","summary":"When parsing data,\ndate strings should likely be converted to `Date` objects.\nBut beware mutations.","date_published":"2023-06-25T12:00:00+03:00","tags":["JavaScript","TypeScript","Zod"]},{"id":"https://mtsknn.fi/blog/harry-potter/","url":"https://mtsknn.fi/blog/harry-potter/","title":"I read all 7 seven Harry Potter books in 33 days","content_html":"<p>I don't know if I have ever read so much in so short time.\nI just liked the books so much.</p>\n<h2 id=\"pages-per-day\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#pages-per-day\">Pages per day</a></h2>\n<p>Here's a table of my reading progress for my own amusement\n(books published by Bloomsbury, 2014 editions):</p>\n<table>\n<thead>\n<tr>\n<th>Book</th>\n<th style=\"text-align:right\">Pages</th>\n<th style=\"text-align:right\">Days</th>\n<th style=\"text-align:right\">Average pages per day</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>1st</td>\n<td style=\"text-align:right\">332</td>\n<td style=\"text-align:right\">5</td>\n<td style=\"text-align:right\">66</td>\n</tr>\n<tr>\n<td>2nd</td>\n<td style=\"text-align:right\">360</td>\n<td style=\"text-align:right\">4</td>\n<td style=\"text-align:right\">90</td>\n</tr>\n<tr>\n<td>3rd</td>\n<td style=\"text-align:right\">462</td>\n<td style=\"text-align:right\">4</td>\n<td style=\"text-align:right\">116</td>\n</tr>\n<tr>\n<td>4th</td>\n<td style=\"text-align:right\">617</td>\n<td style=\"text-align:right\">5</td>\n<td style=\"text-align:right\">123</td>\n</tr>\n<tr>\n<td>5th</td>\n<td style=\"text-align:right\">800</td>\n<td style=\"text-align:right\">6</td>\n<td style=\"text-align:right\">133</td>\n</tr>\n<tr>\n<td>6th</td>\n<td style=\"text-align:right\">542</td>\n<td style=\"text-align:right\">6</td>\n<td style=\"text-align:right\">90</td>\n</tr>\n<tr>\n<td>7th</td>\n<td style=\"text-align:right\">620</td>\n<td style=\"text-align:right\">5</td>\n<td style=\"text-align:right\">124</td>\n</tr>\n<tr>\n<td><strong>Total</strong></td>\n<td style=\"text-align:right\"><strong>3,733</strong></td>\n<td style=\"text-align:right\"><strong>33</strong></td>\n<td style=\"text-align:right\"><strong>113</strong></td>\n</tr>\n</tbody>\n</table>\n<p>There were 33 total days instead of 35\nbecause I started the 4th and 6th books\non the same day as I finished the previous book.</p>\n<h3 id=\"lines-per-page\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#lines-per-page\">Lines per page</a></h3>\n<p>The 4th book's text felt smaller\nthan the first three books' text,\nso I had to check:</p>\n<ol>\n<li>28 lines per page</li>\n<li>28</li>\n<li>28</li>\n<li>33</li>\n<li>34 (!)</li>\n<li>33</li>\n<li>33</li>\n</ol>\n<p>So my &quot;pages per day&quot; comparison table is flawed, meh.</p>\n<p>Fun fact: the 5th book is the longest (800 pages),\nbut it also has the most lines per page,\nso it's really a thick book.</p>\n<h2 id=\"thoughts-and-spoilers\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#thoughts-and-spoilers\">Thoughts and spoilers</a></h2>\n<p>🚨 Spoilers below.</p>\n<h3 id=\"easy-english-weird-words\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#easy-english-weird-words\">Easy English, weird words</a></h3>\n<p>The books were easy to read,\nbut there were also many weird words;\nshows that Rowling's favorite school subject was English\n(it was mentioned at the end of one of the books).</p>\n<p>Some new-for-me words that I still remember\n(these are far from the weirdest words though):</p>\n<ul>\n<li>exasperated (maybe not a new word for me, but I had trouble remembering what this means)</li>\n<li>kissing gate</li>\n<li>palomino</li>\n<li>pensieve</li>\n<li>tabby cat</li>\n</ul>\n<h3 id=\"consistent-point-of-view\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#consistent-point-of-view\">Consistent point of view</a></h3>\n<p>Everything revolves around Harry.\nIf something happens or has happened elsewhere,\nit's explained from Harry's point of view;\ne.g. Harry has visions,\nor someone retells a story for Harry.</p>\n<p>So, the point of view is always consistent,\nwhich makes the books feel coherent.</p>\n<p>I noticed that the last two books break this rule a bit;\nthey have non-Harry points of view in the first chapter(s).\n(I checked and the 1st and 4th books start from different points of view too.)</p>\n<h3 id=\"biggest-plot-weakness\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#biggest-plot-weakness\">Biggest plot weakness</a></h3>\n<p>Some wizards and witches are totally isolated from the rest of the world (from the Muggle world).\nFeels silly.</p>\n<h3 id=\"most-boring-scene\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#most-boring-scene\">Most boring scene</a></h3>\n<p>The 4th book (<em>the Goblet of Fire</em>, the one with the Quidditch World Cup)\nhas long descriptions of various kinds of tents.\nSo boring to read about fancy tents!</p>\n<h3 id=\"most-anxiety-inducing-scene\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#most-anxiety-inducing-scene\">Most anxiety-inducing scene</a></h3>\n<p>When Harry, Ron and Hermione impersonated Ministry of Magic employees\nand infiltrated the Ministry of Magic,\nmy heart was pounding hard,\nand not in a good way.\nI don't know why.\nI guess I was afraid that they were exposed,\nbut I don't know why it was so uncomfortable.</p>\n<p>The robbing of Gringotts Wizarding Bank was a similar scene\nand also anxiety-inducing, but not as bad.\nPossibly because I had already endured the Ministry of Magic scene.</p>\n<p>Both scenes are from the 7th book (<em>the Deathly Hallows</em>).</p>\n<h3 id=\"silly-things\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#silly-things\">Silly things</a></h3>\n<ul>\n<li>Why did Harry, Ron and Hermione have to wear the locket Horcrux at all?!\nWould have been better to keep it safe but not wear it.</li>\n<li>Why and how did Dolores Umbridge survive from the centaurs' wrath?</li>\n<li>Harry and others survived many times from very tight spots at the very last minute/second.\nTypical, but maybe the survivals could have been less obvious or something like that.</li>\n</ul>\n<h3 id=\"my-favorite-character\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#my-favorite-character\">My favorite character</a></h3>\n<p>Dumbledore because he had so wise things to say.</p>\n<p>My buddy Oskari made an interesting general point:\na great discussion in a book can be very short,\nbut it's possible that the author has worked on the lines for a long time (several days or even weeks).</p>\n<h3 id=\"best-girl\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#best-girl\">Best girl</a></h3>\n<p>I was rooting for Harry and Cho Chang;\na shame it didn't work out.</p>\n<p>I guess Ginny was OK too in the end.\nProps for her for the funniest joke:</p>\n<h3 id=\"funniest-joke\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#funniest-joke\">Funniest joke</a></h3>\n<p>The Weasley twins were often funny,\nbut the single funniest joke was\nin the 6th book (<em>the Half-Blood Prince</em>), chapter 25:</p>\n<blockquote>\n<p>The fact that Harry Potter was going out with Ginny Weasley seemed to interest a great number of people, most of them girls, yet Harry found himself newly and happily impervious to gossip over the next few weeks. After all, it made a very nice change to be talked about because of something that was making him happier than he could remember being for a very long time, rather than because he had been involved in horrific scenes of Dark Magic.</p>\n<p>'You'd think people had better things to gossip about,' said Ginny, as she sat on the common-room floor, leaning against Harry's legs and reading the Daily Prophet. 'Three Dementor attacks in a week, and all Romilda Vane does is ask me if it's true you've got a Hippogriff tattooed across your chest.'</p>\n<p>Ron and Hermione both roared with laughter. Harry ignored them.</p>\n<p>'What did you tell her?'</p>\n<p>'I told her it's a Hungarian Horntail,' said Ginny, turning a page of the newspaper idly. 'Much more macho.'</p>\n<p>'Thanks,' said Harry, grinning. 'And what did you tell her Ron's got?'</p>\n<p>'A Pygmy Puff, but I didn't say where.'</p>\n<p>Ron scowled as Hermione rolled around laughing.</p>\n</blockquote>\n<h3 id=\"sweetest-thing\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#sweetest-thing\">Sweetest thing</a></h3>\n<p>Also from the 6th book, chapter 5:</p>\n<blockquote>\n<p>With a sudden exclamation she pointed at the clock's face. Mr Weasley's hand had switched to travelling.</p>\n<p>'He's coming!'</p>\n<p>And sure enough, a moment later there was a knock on the back door. Mrs Weasley jumped up and hurried to it; with one hand on the doorknob and her face pressed against the wood she called softly, 'Arthur, is that you?'</p>\n<p>'Yes,' came Mr Weasley's weary voice. 'But I would say that even if I were a Death Eater, dear. Ask the question!'</p>\n<p>'Oh, honestly …'</p>\n<p>'Molly!'</p>\n<p>'All right, all right … what is your dearest ambition?'</p>\n<p>'To find out how aeroplanes stay up.'</p>\n<p>Mrs Weasley nodded and turned the doorknob, but apparently Mr Weasley was holding tight to it on the other side, because the door remained firmly shut.</p>\n<p>'Molly! I've got to ask you your question first!'</p>\n<p>'Arthur, really, this is just silly …'</p>\n<p>'What do you like me to call you when we're alone together?'</p>\n<p>Even by the dim light of the lantern Harry could tell that Mrs Weasley had turned bright red; he himself felt suddenly warm around the ears and neck, and hastily gulped soup, clattering his spoon as loudly as he could against the bowl.</p>\n<p>'Mollywobbles,' whispered a mortified Mrs Weasley into the crack at the edge of the door.</p>\n<p>'Correct,' said Mr Weasley. 'Now you can let me in.'</p>\n</blockquote>\n<h2 id=\"whats-next\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#whats-next\">What's next?</a></h2>\n<p>Now that I have read the books,\nI'm going to watch the Harry Potter movies.\nI have never watched them,\nexcept maybe the first one in elementary school.</p>\n<p>I'm also going to continue this new hobby of reading non-fiction books.\nNo idea about the next books yet though.</p>\n","summary":"I don't know if I have ever read so much in so short time.\nI just liked the books so much.","date_published":"2023-06-14T12:00:00+03:00","tags":["Books"]},{"id":"https://mtsknn.fi/blog/parse-dont-just-validate/","url":"https://mtsknn.fi/blog/parse-dont-just-validate/","title":"Parsing data is nicer than only validating it","content_html":"<p>Parsing encompasses validation,\nplus it can increase type-safety,\nplus it can transform the data to a better format.\nParse, don't just validate.</p>\n<h2 id=\"validation\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#validation\">Validation</a></h2>\n<p>Validating data means checking if the data matches a certain shape or certain rules.</p>\n<p>For example,\nwhen using <a href=\"https://joi.dev/\" class=\"link link-external\">Joi, a JavaScript validation library</a>,\nyou write a schema and check if some data matches the schema:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> Joi <span class=\"token keyword\">from</span> <span class=\"token string\">'joi'</span>\n\n<span class=\"token keyword\">const</span> userSchema <span class=\"token operator\">=</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">object</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  createdOn<span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">required</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">isoDate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  userName<span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">required</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">min</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  userType<span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">number</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">required</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">integer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">min</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">max</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https://example.com/api/users/123'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> data<span class=\"token operator\">:</span> <span class=\"token builtin\">unknown</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> validationResult <span class=\"token operator\">=</span> userSchema<span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>validationResult<span class=\"token punctuation\">.</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token builtin\">console</span><span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span>validationResult<span class=\"token punctuation\">.</span>error<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> user <span class=\"token operator\">=</span> validationResult<span class=\"token punctuation\">.</span>value\n  <span class=\"token comment\">//    ^? any</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Validation is essentially a binary check:\nthe data either is valid or it isn't.</p>\n<p>Well, you do get descriptive validation error messages when using Joi.</p>\n<p>But if the validation passes,\nyou are none the wiser about the validated data,\nat least in terms of type-safety:\nthe original <code>data</code> variable has still type <code>unknown</code>,\nand <code>validationResult.value</code> has type <code>any</code>.</p>\n<p>While validating data is certainly useful,\nthe result can be made more useful by parsing the data instead of only validating it.</p>\n<h2 id=\"parsing\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#parsing\">Parsing</a></h2>\n<p>Parsing means processing some input\nand converting it to a more precise output.</p>\n<p>Parsing does validation plus more:\nparsing = validating data + increasing type-safety + transforming data.</p>\n<h3 id=\"parsing-encompasses-validation\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#parsing-encompasses-validation\">Parsing encompasses validation</a></h3>\n<p>Validation is a more-or-less implicit step of parsing:\nif the input data isn't valid (from the parser's perspective),\nthe parsing can fail.</p>\n<p>Examples:</p>\n<ul>\n<li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse\" class=\"link link-external\"><code>JSON.parse</code></a>\nparses a JSON string,\nand throws if the input is not valid JSON.</li>\n<li><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse\" class=\"link link-external\"><code>Date.parse</code></a>\nparses a date string and returns a timestamp,\nor <code>NaN</code> if the input is not a valid date.\n<ul>\n<li>By the way,\n<a href=\"/blog/javascripts-date-constructor-is-bad-at-parsing-dates/\" class=\"link\"><code>Date.parse</code> is quite bad at parsing dates</a>.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"parsing-can-increase-type-safety\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#parsing-can-increase-type-safety\">Parsing can increase type-safety</a></h3>\n<p>In the Joi example above,\nJoi could in theory be able to know the result type.</p>\n<p>For example,\nif an object schema contains a property with the value of <code>Joi.string()</code>,\nJoi could infer the type of that property to be <code>string | undefined</code>.<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup></p>\n<p>But Joi doesn't do that.\nIt throws away the potential type knowledge acquired during validation.</p>\n<h4>Example: Joi vs Zod</h4>\n<p>One alternative to Joi is\n<a href=\"https://zod.dev/\" class=\"link link-external\">Zod, a TypeScript validation library</a>.</p>\n<p>On the surface,\nZod is used similarly to Joi,\nbut the critical difference is that Zod can infer the return type of the schema:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token deleted-sign deleted language-ts\"><span class=\"token prefix deleted\">-</span><span class=\"token keyword\">import</span> Joi <span class=\"token keyword\">from</span> <span class=\"token string\">'joi'</span>\n<span class=\"token prefix deleted\">-</span>\n<span class=\"token prefix deleted\">-</span><span class=\"token keyword\">const</span> userSchema <span class=\"token operator\">=</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">object</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n<span class=\"token prefix deleted\">-</span>  createdOn<span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">required</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">isoDate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token prefix deleted\">-</span>  userName<span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">required</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">min</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token prefix deleted\">-</span>  userType<span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">number</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">required</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">integer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">min</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">max</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token prefix deleted\">-</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span>\n<span class=\"token inserted-sign inserted language-ts\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> z <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'zod'</span>\n<span class=\"token prefix inserted\">+</span>\n<span class=\"token prefix inserted\">+</span><span class=\"token keyword\">const</span> userSchema <span class=\"token operator\">=</span> z<span class=\"token punctuation\">.</span><span class=\"token function\">object</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n<span class=\"token prefix inserted\">+</span>  createdOn<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">datetime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token prefix inserted\">+</span>  userName<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">min</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token prefix inserted\">+</span>  userType<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token function\">number</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">int</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">min</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">max</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token prefix inserted\">+</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix inserted\">+</span><span class=\"token keyword\">type</span> <span class=\"token class-name\">User</span> <span class=\"token operator\">=</span> z<span class=\"token punctuation\">.</span>infer<span class=\"token operator\">&lt;</span><span class=\"token keyword\">typeof</span> userSchema<span class=\"token operator\">></span>\n<span class=\"token prefix inserted\">+</span><span class=\"token comment\">//   ^? {</span>\n<span class=\"token prefix inserted\">+</span><span class=\"token comment\">//        createdOn: string</span>\n<span class=\"token prefix inserted\">+</span><span class=\"token comment\">//        userName: string</span>\n<span class=\"token prefix inserted\">+</span><span class=\"token comment\">//        userType: number</span>\n<span class=\"token prefix inserted\">+</span><span class=\"token comment\">//      }</span>\n</span>\n<span class=\"token unchanged language-ts\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https://example.com/api/users/123'</span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> data<span class=\"token operator\">:</span> <span class=\"token builtin\">unknown</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n</span>\n<span class=\"token deleted-sign deleted language-ts\"><span class=\"token prefix deleted\">-</span><span class=\"token keyword\">const</span> validationResult <span class=\"token operator\">=</span> userSchema<span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span>\n<span class=\"token prefix deleted\">-</span>\n<span class=\"token prefix deleted\">-</span><span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>validationResult<span class=\"token punctuation\">.</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix deleted\">-</span>  <span class=\"token builtin\">console</span><span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span>validationResult<span class=\"token punctuation\">.</span>error<span class=\"token punctuation\">)</span>\n<span class=\"token prefix deleted\">-</span><span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix deleted\">-</span>  <span class=\"token keyword\">const</span> user <span class=\"token operator\">=</span> validationResult<span class=\"token punctuation\">.</span>value\n<span class=\"token prefix deleted\">-</span>  <span class=\"token comment\">//    ^? any</span>\n<span class=\"token prefix deleted\">-</span><span class=\"token punctuation\">}</span>\n</span>\n<span class=\"token inserted-sign inserted language-ts\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">const</span> parsingResult <span class=\"token operator\">=</span> userSchema<span class=\"token punctuation\">.</span><span class=\"token function\">safeParse</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span>\n<span class=\"token prefix inserted\">+</span>\n<span class=\"token prefix inserted\">+</span><span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>parsingResult<span class=\"token punctuation\">.</span>success<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix inserted\">+</span>  <span class=\"token keyword\">const</span> user <span class=\"token operator\">=</span> parsingResult<span class=\"token punctuation\">.</span>data\n<span class=\"token prefix inserted\">+</span>  <span class=\"token comment\">//    ^? User</span>\n<span class=\"token prefix inserted\">+</span><span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix inserted\">+</span>  <span class=\"token builtin\">console</span><span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span>parsingResult<span class=\"token punctuation\">.</span>error<span class=\"token punctuation\">)</span>\n<span class=\"token prefix inserted\">+</span><span class=\"token punctuation\">}</span>\n</span></span></code></pre>\n<p>The <code>user</code> variable, which is the result of successful parsing,\nhas now type <code>User</code>, which is inferred from the Zod schema.</p>\n<h4>What about <code>JSON.parse</code>?</h4>\n<p>I used <code>JSON.parse</code> as an example above\nwhen talking about the validation step of parsing.\nBut hmm, why doesn't <code>JSON.parse</code> increase any type-safety...?</p>\n<p>Maybe <code>JSON.parse</code> should be thought of as a very high-level parser.\nThe TypeScript <em>compiler</em> can't have visibility on things happening at <em>runtime</em>,\nlike what's passed to <code>JSON.parse</code>.\nIf you want type-safety,\nyou need to be more specific what you ask for,\ne.g. by using a type-aware runtime parser like Zod.</p>\n<h3 id=\"parsing-can-transform-data\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#parsing-can-transform-data\">Parsing can transform data</a></h3>\n<p>So far I have talked only about validation and type-safety.\nWhat really separates parsing from validation (type-safe or not) is data transformations\n(or &quot;data mapping&quot; or &quot;data reserialization&quot;).</p>\n<p>Transforming data means modifying the data to a better format.\nNot by mutating the input data<sup><a aria-label=\"footnote 2\" class=\"link\" href=\"#fn-2\" id=\"fnref-2\">[2]</a></sup>,\nbut by returning a different shape than what was passed to the parser.</p>\n<p>What a &quot;better format&quot; means is up to you.\nFor example, you can:</p>\n<ul>\n<li>Trim strings.</li>\n<li>Convert values to different types,\ne.g. numerical strings to <code>number</code>s,\nor <a href=\"/blog/date-objects-vs-date-strings/\" class=\"link\">date strings to <code>Date</code> objects</a>.</li>\n<li>Set default values for optional or nullable fields.</li>\n<li>Transform object keys from <code>PascalCase</code> or <code>snake_case</code> (neither are JavaScript-y) to <code>camelCase</code>.</li>\n<li>Rename object keys from a foreign language to English.</li>\n<li>Rename object keys to words that are already used in your code base\nto avoid having multiple words for the same thing (data coherency);\ne.g. <code>userId</code> vs <code>personId</code>.</li>\n<li>Compute/derive values from other values;\ne.g. construct <code>fullName</code> by combining <code>firstName</code> and <code>lastName</code>.</li>\n<li>Simplify object structures,\ne.g. change <code>Array&lt;{ key: string; value: number }&gt;</code> to <code>Record&lt;string, number&gt;</code>.</li>\n<li>Convert comma-separated strings to arrays,\ne.g. <code>&quot;1,2,3&quot;</code> to <code>[1, 2, 3]</code>.</li>\n<li>Convert number unions to string unions\n(see the <code>userSchema</code> example below).</li>\n</ul>\n<p>The point is to modify the data so that it looks like you'd want it to look.</p>\n<p>It's not fun to work with messy data,\nbut if you transform the data to a nicer format right after receiving it:</p>\n<ul>\n<li>You need to deal with the messy data only once.</li>\n<li>The transformations are centralized to one place\ninstead of being scattered around everywhere.</li>\n<li>You can write business logic against the ideally shaped data\n(as opposed to writing business logic against &quot;someone else's data&quot;).</li>\n</ul>\n<h4>Example: <code>userSchema</code></h4>\n<p>In the Joi vs Zod example above,\n<code>userSchema</code> and the inferred type look like this:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> z <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'zod'</span>\n\n<span class=\"token keyword\">const</span> userSchema <span class=\"token operator\">=</span> z<span class=\"token punctuation\">.</span><span class=\"token function\">object</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  createdOn<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">datetime</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  userName<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">min</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  userType<span class=\"token operator\">:</span> z<span class=\"token punctuation\">.</span><span class=\"token function\">number</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">int</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">min</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">max</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">type</span> <span class=\"token class-name\">User</span> <span class=\"token operator\">=</span> z<span class=\"token punctuation\">.</span>infer<span class=\"token operator\">&lt;</span><span class=\"token keyword\">typeof</span> userSchema<span class=\"token operator\">></span>\n<span class=\"token comment\">//   ^? {</span>\n<span class=\"token comment\">//        createdOn: string</span>\n<span class=\"token comment\">//        userName: string</span>\n<span class=\"token comment\">//        userType: number</span>\n<span class=\"token comment\">//      }</span>\n</span></code></pre>\n<p>The <code>User</code> type is quite messy.\nIf the data is coming from an external API,\nthe data itself can't be changed...\nbut the parsed data can be improved by:</p>\n<ul>\n<li>Converting the <code>createdOn</code> string to a <code>Date</code> object,\nso you don't need to repeatedly do a conversion from a date string to a <code>Date</code> object.\n<a href=\"/blog/date-objects-vs-date-strings/\" class=\"link\"><code>Date</code> objects are nicer to work with than date strings.</a></li>\n<li>Converting the <code>userType</code> number to a string union of <code>'admin' | 'moderator' | 'user'</code>.\nA string union like that is much clearer,\nplus then you don't need to repeatedly do a conversion from a number to a string.</li>\n<li>Renaming <code>userName</code> (uppercase N) to <code>username</code> (lowercase n)\nbecause &quot;username&quot; is often spelled as a single word.</li>\n<li>Renaming <code>userType</code> to <code>role</code> because <code>userType</code> sounds redundant,\nand <code>type</code> would sound a bit too generic.</li>\n</ul>\n<p>Like so:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">User</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  createdOn<span class=\"token operator\">:</span> Date\n  role<span class=\"token operator\">:</span> <span class=\"token string\">'admin'</span> <span class=\"token operator\">|</span> <span class=\"token string\">'moderator'</span> <span class=\"token operator\">|</span> <span class=\"token string\">'user'</span>\n  username<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">parseUser</span><span class=\"token punctuation\">(</span>data<span class=\"token operator\">:</span> <span class=\"token builtin\">unknown</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> User <span class=\"token operator\">|</span> ParseError <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// Validate and transform `data`</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p><em>How</em> you transform the data is an implementation detail.\nZod has means for transforming data (another blog post coming later);\nyou could also transform the data manually.</p>\n<h2 id=\"fuzzy-terminology\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#fuzzy-terminology\">Fuzzy terminology</a></h2>\n<p>What is parsing?\nWhat is validation?</p>\n<p>Above I said that\nparsing = validating data + increasing type-safety + transforming data.\nThis is based on other sources (see <a href=\"#further-resources\" class=\"link link-anchor\">further resources</a> below).</p>\n<p>If the data is not transformed,\nI guess it could still be called just validation;\nbe it &quot;type-ignorant&quot; validation (think of Joi)\nor type-aware validation (think of Zod).</p>\n<p>In fact,\nJoi and Zod are both called validation libraries\neven though they also support data transformations.\nBut that's also a bit confusing.</p>\n<h3 id=\"practical-guidelines\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#practical-guidelines\">Practical guidelines</a></h3>\n<ul>\n<li>If your intention is to transform the data,\nmake sure you call it parsing.\nCalling it validation would be confusing.</li>\n<li>If your intention is to do only validation:\n<ul>\n<li>Call it validation or parsing;\nchoose whichever term and stick to it (be consistent).\nCalling it validation would distinguish it from parsing that transforms data,\nthough I don't know if that would valuable.</li>\n<li>If you call it validation,\nmake sure you don't transform the data (accidentally or deliberately);\nit would be confusing.</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<p>My other blog posts on this topic:</p>\n<ul>\n<li><a href=\"/blog/date-objects-vs-date-strings/\" class=\"link\"><em><code>Date</code> objects are nicer to work with than date strings</em></a></li>\n</ul>\n<p>More coming later; I like this topic.</p>\n<h3 id=\"main-inspiration\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#main-inspiration\">Main inspiration</a></h3>\n<p>This blog post is largely inspired by the\n<a href=\"https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/\" class=\"link link-external\"><em>Parse, don't validate</em> blog post by Alexis King</a>.\nHer post has examples in Haskell,\nwhich I'm not familiar with,\nbut the post was still easy to read.</p>\n<p>These snippets resonated with me the most:</p>\n<ul>\n<li>Validation vs parsing:\n<blockquote>\n<p>[I]n my mind, the difference between validation and parsing lies almost entirely in how information is preserved. [...] Both of [them] check the same thing, but [parsing] gives the caller access to the information it learned, while [validating] just throws it away.</p>\n</blockquote>\n</li>\n<li>What is parsing:\n<blockquote>\n<p>Consider: what is a parser? Really, a parser is just a function that consumes less-structured input and produces more-structured output. [...] Under this flexible definition, parsers are an incredibly powerful tool: they allow discharging checks on input up-front, right on the boundary between a program and the outside world, and once those checks have been performed, they never need to be checked again!</p>\n</blockquote>\n</li>\n<li>Beware &quot;shotgun parsing&quot;:\n<blockquote>\n<p>Ad-hoc validation leads to a phenomenon that the <a href=\"http://langsec.org/\" class=\"link link-external\">language-theoretic security</a> field calls <em>shotgun parsing</em>. [...]</p>\n<blockquote>\n<p>Shotgun parsing is a programming antipattern whereby parsing and input-validating code is mixed with and spread across processing code [...].</p>\n</blockquote>\n<p>[...] In other words, a program that does not parse all of its input up front runs the risk of acting upon a valid portion of the input, discovering a different portion is invalid, and suddenly needing to roll back whatever modifications it already executed in order to maintain consistency. Sometimes this is possible – such as rolling back a transaction in an RDBMS – but in general it may not be.</p>\n<p>It may not be immediately apparent what shotgun parsing has to do with validation – after all, if you do all your validation up front, you mitigate the risk of shotgun parsing. The problem is that validation-based approaches make it extremely difficult or impossible to determine if everything was actually validated up front or if some of those so-called &quot;impossible&quot; cases might actually happen. The entire program must assume that raising an exception anywhere is not only possible, it's regularly necessary.</p>\n<p>Parsing avoids this problem by stratifying the program into two phases – parsing and execution – where failure due to invalid input can only happen in the first phase. The set of remaining failure modes during execution is minimal by comparison, and they can be handled with the tender care they require.</p>\n</blockquote>\n</li>\n<li>Guidelines for parsing –\nthe blog post contains lots of great points;\nthese few resonated with me the most:\n<ul>\n<li>\n<blockquote>\n<p>Get your data into the most precise representation you need as quickly as you can. Ideally, this should happen at the boundary of your system, before <em>any</em> of the data is acted upon.</p>\n<p>If one particular code branch eventually requires a more precise representation of a piece of data, parse the data into the more precise representation as soon as the branch is selected.</p>\n</blockquote>\n</li>\n<li>\n<blockquote>\n<p>[W]rite functions on the data representation you <em>wish</em> you had, not the data representation you are given. The design process then becomes an exercise in bridging the gap, often by working from both ends until they meet somewhere in the middle.</p>\n</blockquote>\n</li>\n<li>\n<blockquote>\n<p>Don't be afraid to parse data in multiple passes. Avoiding shotgun parsing just means you shouldn't act on the input data before it's fully parsed, not that you can't use some of the input data to decide how to parse other input data. Plenty of useful parsers are context-sensitive.</p>\n</blockquote>\n</li>\n</ul>\n</li>\n</ul>\n<p>After reading Alexis King's blog post,\nI wasn't sure of the relationship between parsing and validation.\nThese Hacker News (HN) comments were helpful in realizing that\n&quot;parse, don't validate&quot; doesn't mean &quot;parse data instead of validating it&quot;;\nit means &quot;parse data instead of <em>only</em> validating it&quot;:</p>\n<ul>\n<li><a href=\"https://news.ycombinator.com/item?id=21478870\" class=\"link link-external\">An HN comment by lexi-labmda (Alexis King)</a>:\n<blockquote>\n<p>The idea really is that parsing subsumes validation. If you're parsing, you don't have to validate, because validation is a natural side-effect of the parsing process. And indeed, I think that's the very thesis of the blog post: parsing <em>is</em> validation, just without throwing away all the information you learned once you've finished!</p>\n</blockquote>\n</li>\n<li>An excerpt from <a href=\"https://news.ycombinator.com/item?id=21477645\" class=\"link link-external\">an HN comment by danharaj</a>:\n<blockquote>\n<p>The point is not that &quot;parsing&quot; and &quot;validating&quot; are distinct concepts, but that they're two different points of view on the same problem.</p>\n</blockquote>\n</li>\n<li><a href=\"https://news.ycombinator.com/item?id=27644989\" class=\"link link-external\">An HN comment by b3morales</a>:\n<blockquote>\n<p>You seem to suggest that it's possible to parse <em>without</em> validating, which I'm not sure I follow. Surely validation is just one of the phases or steps of parsing?</p>\n</blockquote>\n</li>\n<li><a href=\"https://news.ycombinator.com/item?id=27646359\" class=\"link link-external\">An HN comment by nsajko</a>:\n<blockquote>\n<p>The point is that validation is (or should/can be) a byproduct of parsing. I.e., you shouldn't &quot;do both&quot;, rather the validation should be encompassed by the parsing, as much as it makes sense.</p>\n</blockquote>\n</li>\n<li>An excerpt from <a href=\"https://news.ycombinator.com/item?id=35055151\" class=\"link link-external\">an HN comment by friendzis</a>:\n<blockquote>\n<p>Validation happens during parsing implicitly: parser will either return a <em>valid</em> object or throw an error, but parsing has an added benefit that the end result is a datum of a known datatype.</p>\n</blockquote>\n</li>\n</ul>\n<h3 id=\"other-sources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#other-sources\">Other sources</a></h3>\n<p>Other helpful things I have stumbled upon,\nwith a few grammatical fixes:</p>\n<ul>\n<li>An excerpt from <a href=\"https://lobste.rs/s/ot2mea/parse_don_t_validate_2019#c_szw965\" class=\"link link-external\">a Lobsters comment by kevincox</a>:\n<blockquote>\n<p>When accepting untrusted input you should not validate it, and accept or reject the user input. You should instead parse and reserialize the input. This ensures that you will only have data that you wrote yourself. It is also natural to throw away unknown fields and simply any odd formatting.</p>\n</blockquote>\n</li>\n<li>An excerpt from <a href=\"https://lobste.rs/s/ot2mea/parse_don_t_validate_2019#c_czsiio\" class=\"link link-external\">a Lobsters comment by jbert</a>:\n<blockquote>\n<p>Is [the &quot;Parse, don't validate&quot; idea] fundamentally the same idea as &quot;make invalid states unrepresentable&quot;?</p>\n<p>I.e. don't have a corner case of your type where you could have a value which isn't valid (like the empty list), instead tighten your type so that every possible value of the type is valid.</p>\n<p>Looked at this way, any time you have a gap (where your type is too loose and allows invalid states) the consumer of the value of that type needs to check them again. This burden is felt everywhere the type is used.</p>\n<p>If you do the check to ensure you haven't fallen into the gap, take the opportunity to tighten the type to get rid of the gap. I.e. make the invalid state unrepresentable.</p>\n</blockquote>\n</li>\n<li>An excerpt from <a href=\"https://lobste.rs/s/8p0q7g/test_against_what_won_t_change#c_s5ecsd\" class=\"link link-external\">a Lobsters comment by Axman6</a>:\n<blockquote>\n<p>As a Haskell developer, I've spent a lot of time looking at unit test suites for other languages and thinking &quot;why are you testing that? your type system should make that impossible&quot;, and coming to the realisation that for many languages, their test suite is their type system – but it is an incomplete, sometimes buggy one limited by what the developer was able to predict could go wrong.</p>\n<p>I've always tried to make programs where only valid business logic is possible, and doing this relies heavily on the use of sum types to be precise about what is allowable – I was talking earlier today to someone on IRC about the difference between validating (a.k.a writing code that works with [booleans] to gate progress to other code) and parsing (writing code that produces valid values – perfectly summed up in Alexis King's <a href=\"https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/\" class=\"link link-external\">Parse, don't validate</a>).</p>\n<p>At a previous job, we did this religiously, data coming into the program was parsed (in this case from SQS queues), such that we knew in the rest of the system that all our preconditions had been satisfied, and only valid data was allowed within the inner shell of the app. [...]</p>\n<p>The rest of the program was basically implemented in a way that it was very difficult to write incorrect code, all alternatives were represented as subtypes [...].</p>\n</blockquote>\n</li>\n<li>An excerpt from <a href=\"https://itnext.io/parse-dont-validate-incoming-data-in-typescript-d6d5bfb092c8\" class=\"link link-external\"><em>Parse, don't validate, incoming data in TypeScript</em> by Elias Nygren</a>:\n<blockquote>\n<p>The <em>&quot;parse, don't validate&quot;</em> mantra is all about parsing incoming data to a specific type, or failing in a controlled manner if the parsing fails. It's about using trusted, safe and typed data structures inside your code and making sure all incoming data is handled at the very edges of your programs. Don't pass incoming data deep into your code, parse it right away and fail fast if needed.</p>\n<p>Parsing is better than just validation because parsing forces you to explicitly handle all incoming data. It gives you a type safe way [of] working and makes it hard to pass around malicious content around your applications and data stores. However, it is true that parsing often includes validating the data.</p>\n</blockquote>\n</li>\n<li>Excerpts from <a href=\"https://blog.ploeh.dk/2022/08/22/can-types-replace-validation/\" class=\"link link-external\"><em>Can types replace validation?</em> by Mark Seemann</a>:\n<ul>\n<li>\n<blockquote>\n<p>The naive take on validation is to answer the question: <em>Is that data valid or invalid?</em> Notice the binary nature of the question. It's either-or.</p>\n</blockquote>\n</li>\n<li>\n<blockquote>\n<p>This changes focus on validation. No longer is validation a <em>true/false</em> question. Validation is a function from less-structured data to more-structured data. <a href=\"https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/\" class=\"link link-external\">Parse, don't validate.</a></p>\n</blockquote>\n<ul>\n<li>The &quot;This&quot; at the beginning refers to &quot;applicative validation&quot; (I don't know what that is),\nbut I think it would very well refer to parsing as well\n(i.e. &quot;Parsing changes focus on validation&quot;);\nthe quote would still make sense.</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>An excerpt from <a href=\"https://donnywinston.com/posts/shotgun-semantics/\" class=\"link link-external\"><em>Shotgun Semantics</em> by Donny Winston</a>:\n<blockquote>\n<p>Developers often resort to <em>shotgun parsing</em>: scattering data checks and fallback values in various places throughout the system's main logic.</p>\n<p>The habit of scattering parser-like behaviour throughout an application's code and the resulting inconsistencies in data handling can often lead not just to annoying complications and bugs, but also security vulnerabilities.</p>\n</blockquote>\n</li>\n<li>An excerpt from <a href=\"https://old.reddit.com/r/typescript/comments/vaukc1/has_your_team_adopted_typescript_how_is_it_going/ic4kzpx/\" class=\"link link-external\">a deleted user's comment on an /r/typescript post <em>Has your team adopted TypeScript? How is it going?</em></a>:\n<blockquote>\n<p>I joined a team 6 months ago that had recently adopted TypeScript to a fairly new (2 years old) project. [...]</p>\n<p>People didn't really know why they were using it. They would create interfaces for objects coming over APIs (good), but would make them have slightly differently named fields, like <code>mainId</code> would be named <code>main_id</code> in the interface (bad). This resulted in pointless chunks of code to &quot;convert&quot; the API object to the front-end interface.</p>\n</blockquote>\n<ul>\n<li>A reply by monnef:\n<blockquote>\n<p>In some languages (like Haskell) this is the recommended approach – separate types for API and rest of the application. When the API changes, you don't have to change all code which works with the type, just the convert functions. Even in TS, I really don't like reusing API types (<a href=\"https://gitlab.com/monnef/oats\" class=\"link link-external\">generated</a> in our case) for forms and a few other places, because more often than not the types aren't actually the same (e.g. <code>null</code> vs <code>undefined</code>, <code>number</code> vs <code>string</code> and so on).</p>\n</blockquote>\n</li>\n<li>Another reply by pm_me_ur_happy_traiI:\n<blockquote>\n<p>I actually encourage my team to do exactly this. Back-end APIs are optimized to be communicated over the wire. They need to be turned into something that works with front-end conventions and suits the needs of our app.</p>\n<p>We build our front-ends using data structures that make sense to the front-end, and there's usually a function to turn the back-end API response into that. The added upside is that if the back-end changes, we just need to fix the one function as opposed to renaming properties all over our code base.</p>\n</blockquote>\n</li>\n</ul>\n</li>\n<li>An excerpt from <a href=\"https://blog.theodo.com/2023/04/parsing-rest-api-responses-in-typescript-using-zod/\" class=\"link link-external\"><em>Parsing REST API Responses in TypeScript using Zod</em> by Matt Newhall</a>:\n<blockquote>\n<p>The [<em>&quot;parse, don't validate&quot;</em>] concept boils down to the underlying assumption in validation that a type can have valid and invalid instances. Let's take an example of validating a type, <code>T</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function-variable function\">validateType</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token constant\">T</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token builtin\">boolean</span>\n</span></code></pre>\n<p>In this case, <code>validateType</code> takes a type <code>T</code> and then by evaluating it returns a boolean stating if the incoming object was a valid object of type <code>T</code> or not. However, this means that invalid instances of <code>T</code> can exist. Alternatively, using parsing:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function-variable function\">parseType</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token builtin\">unknown</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token constant\">T</span> <span class=\"token operator\">|</span> Error\n</span></code></pre>\n<p>We can take some data of an unknown type, and check if this matches what is expected of type <code>T</code>. From this, we can output a guaranteed type <code>T</code>, or handle the error in the event that it is not. This makes the schema checking system more TypeScript-esque, making it more akin to a type guard than blindly assuming a variable's type.</p>\n</blockquote>\n<ul>\n<li>An interesting point that I hadn't considered:\n&quot;[Validating instead of parsing] means that invalid instances of <code>T</code> can exist.&quot;\nOn the other hand, shouldn't <code>validateType</code> take an <code>unknown</code> param, not a <code>T</code> param? Hmm...</li>\n</ul>\n</li>\n<li>An excerpt from <a href=\"https://eweise.com/post/sideeffects/\" class=\"link link-external\"><em>Pushing Side Effects to the Side</em> by Eric Weise</a>:\n<blockquote>\n<p>Functional programming provides a treasure trove of useful patterns, one of which is pushing side effects to the edges of your application. Side effects are functions that have non-deterministic behavior. Examples include making an HTTP call or persisting data to the database. [...] By isolating these types of calls, we can more easily test our code. [...]</p>\n<p>How many methods could you turn into simple pure function calls simply by removing side effect calls such as repositories from your business logic? Most projects I have worked on just sprinkle database calls throughout service layer.</p>\n<p>In general the pattern I try to follow is;</p>\n<ul>\n<li>Retrieve all the data necessary to perform the business logic (side effect code).</li>\n<li>Perform the business logic using in-memory data (pure functions).</li>\n<li>Persist changes to the database (side effect code).</li>\n</ul>\n<p>These three steps can't be followed every time but when they can, the code will be much easier to test.</p>\n</blockquote>\n<ul>\n<li>I think &quot;pushing side effects to the side&quot; doesn't benefit only testing,\nso &quot;the code will be much easier to test&quot; could also read\n&quot;the code will be much <em>clearer overall</em>.&quot;</li>\n</ul>\n</li>\n<li>Slogan of the <a href=\"https://github.com/GervinFung/parse-dont-validate\" class=\"link link-external\"><code>parse-dont-validate</code> TypeScript library</a>:\n<blockquote>\n<p>Validating data acts as a gatekeeper, parsing them into meaningful data types adds valuable information to raw data.</p>\n</blockquote>\n</li>\n<li><a href=\"https://twitter.com/kwasniew/status/1426866923334250498\" class=\"link link-external\">A tweet by Mateusz Kwaśniewski: <em>Parse, don't validate – TypeScript edition</em></a>\n<ul>\n<li>Features Joi and Zod, which inspired me to use them in this blog post.</li>\n</ul>\n</li>\n<li><a href=\"https://github.com/colinhacks/zod/discussions/962\" class=\"link link-external\">A GitHub discussion under the Zod project: <em>Confusing terms – parse vs validate</em></a></li>\n</ul>\n<h2 id=\"changelog\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#changelog\">Changelog</a></h2>\n<ul>\n<li>2023-06-13: Blog post published</li>\n<li>2023-06-25: Added links to today's blog post\n<a href=\"/blog/date-objects-vs-date-strings/\" class=\"link\"><em><code>Date</code> objects are nicer to work with than date strings</em></a></li>\n</ul>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>The corresponding type of <code>Joi.string()</code> would be <code>string | undefined</code>,\nbecause in Joi, all values are optional by default.\nI find it a bit confusing.\nThe type of <code>Joi.string().required()</code> would be <code>string</code>. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n<li id=\"fn-2\"><p>I wonder if there's ever a good reason for a parser to mutate the input data. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-2\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"Parsing encompasses validation,\nplus it can increase type-safety,\nplus it can transform the data to a better format.\nParse, don't just validate.","date_published":"2023-06-13T12:00:00+03:00","date_modified":"2023-06-25T12:00:00+03:00","tags":["JavaScript","Joi","TypeScript","Zod"]},{"id":"https://mtsknn.fi/blog/epub-to-azw3/","url":"https://mtsknn.fi/blog/epub-to-azw3/","title":"Auto-converting EPUB files to AZW3 instead of MOBI in Calibre","content_html":"<p>MOBI is an old e-book format.\nAZW3 is a newer and apparently better format;\nfor instance, it supports embedded fonts.\nHere's how to configure Calibre to prefer the better format.</p>\n<ul>\n<li><strong>Global settings</strong>\n<ol>\n<li>In Calibre, go to Preferences → Behavior</li>\n<li>Change &quot;Preferred output format&quot; from MOBI to AZW3</li>\n</ol>\n</li>\n<li><strong>Device settings</strong> might override global settings,\nso check device settings too\n<ol>\n<li>Plug in your device (via USB)</li>\n<li>In Calibre, select Device → Configure this device</li>\n<li>Under &quot;Select available formats and their order for this device,&quot;\nmove azw3 to the top\n<ul>\n<li>My old order was this:\n<ol>\n<li>azw</li>\n<li>mobi</li>\n<li><strong>azw3</strong></li>\n<li>...</li>\n</ol>\n</li>\n<li>So I moved azw3 to the top:\n<ol>\n<li><strong>azw3</strong></li>\n<li>azw</li>\n<li>mobi</li>\n<li>...</li>\n</ol>\n</li>\n</ul>\n</li>\n</ol>\n</li>\n</ul>\n","summary":"MOBI is an old e-book format.\nAZW3 is a newer and apparently better format;\nfor instance, it supports embedded fonts.\nHere's how to configure Calibre to prefer the better format.","date_published":"2023-04-29T12:00:00+03:00","tags":["Books"]},{"id":"https://mtsknn.fi/blog/bye-acsm-and-drm/","url":"https://mtsknn.fi/blog/bye-acsm-and-drm/","title":"De-ACSM'ing and de-DRM'ing e-books for fun (but not profit)","content_html":"<p>A reminder for myself how to make library e-books pleasant to use\nwith the help of Calibre and two Calibre plugins.</p>\n<h2 id=\"problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problem\">Problem</a></h2>\n<p>My local library provides many e-books for free, which is great,\nbut actually getting those e-books to my Kindle Paperwhite is a hassle:</p>\n<ol>\n<li>Download an EPUB file (so the button says on the library site)\n– whoops, it's actually an ACSM file.</li>\n<li>Download Adobe Digital Editions (ADE) software to open the ACSM file\n– whoops, ADE requires an Adobe account.</li>\n<li>Create an Adobe account.</li>\n<li>Get an EPUB file from ADE.</li>\n<li>Upload the EPUB file to Kindle Paperwhite\n– whoops, Kindle doesn't support EPUB files.</li>\n<li>Convert the EPUB file to a Kindle-supported format (e.g. MOBI)\n– whoops, the EPUB file is DRM-protected so it's not that simple.</li>\n<li>Remove the DRM protection by using a shady website,\nprobably risking leaking your personal info (likely stored in the DRM-protected EPUB file).</li>\n<li><em>Finally</em> upload the DRM-free, Kindle-compatible file to Kindle.</li>\n</ol>\n<p>I don't actually know the exact process\nbecause I have never done it the &quot;official way.&quot;\nJust too complicated, too much work, too annoying.</p>\n<h2 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h2>\n<h3 id=\"initial-setup\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#initial-setup\">Initial setup</a></h3>\n<p>Needs to be done only once.</p>\n<ol>\n<li>Download and install <a href=\"https://calibre-ebook.com/\" class=\"link link-external\">Calibre (e-book management software)</a>.</li>\n<li>Download and install <a href=\"https://github.com/noDRM/DeDRM_tools\" class=\"link link-external\">DeDRM_tools plugin for Calibre</a>.\n<ul>\n<li>This plugin will automatically remove DRM protection from EPUB files that you drag-and-drop to Calibre.</li>\n<li>noDRM's fork (linked to above) is up-to-date; the original is not.</li>\n<li>Be sure to install this before the next plugin,\nor you might need to do one extra step manually when configuring the next plugin.</li>\n</ul>\n</li>\n<li>Download, install and configure <a href=\"https://github.com/Leseratte10/acsm-calibre-plugin\" class=\"link link-external\">ACSM Input plugin for Calibre</a>.\n<ul>\n<li>This plugin will automatically convert ACSM files that you drag-and-drop to Calibre into EPUB files\n<ul>\n<li>No need for ADE (Adobe Digital Editions software).</li>\n<li>No need for an Adobe account because you can create an anonymous authorization in the plugin's settings.\n<ul>\n<li>Make sure to <a href=\"https://github.com/Leseratte10/acsm-calibre-plugin/discussions/52\" class=\"link link-external\">back up the authorization data</a>,\njust in case.</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>(Optional: <a href=\"/blog/epub-to-azw3/\" class=\"link\">Configure Calibre to auto-convert EPUB files to AZW3 instead of MOBI</a>.)</li>\n</ol>\n<h3 id=\"e-book-to-kindle\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#e-book-to-kindle\">E-book to Kindle</a></h3>\n<ol>\n<li>Download an ACSM file from library site.</li>\n<li>Drag-and-drop the ACSM file to Calibre.</li>\n<li>Plug in Kindle to computer (via USB).</li>\n<li>Select the book in Calibre and click &quot;Send to device.&quot;</li>\n<li>Eject Kindle.</li>\n<li>Go read and enjoy the book!</li>\n</ol>\n<p>All steps except the 2nd one are fluff,\nso this is actually a one-step process\n(just drag-and-drop to Calibre).\nNice and easy.</p>\n","summary":"A reminder for myself how to make library e-books pleasant to use\nwith the help of Calibre and two Calibre plugins.","date_published":"2023-04-28T12:00:00+03:00","date_modified":"2023-04-29T12:00:00+03:00","tags":["Books"]},{"id":"https://mtsknn.fi/blog/tricky-email-aliases/","url":"https://mtsknn.fi/blog/tricky-email-aliases/","title":"Sending (duplicate) emails to email aliases can be tricky","content_html":"<p>If you send a single email to a bunch of email addresses,\nand some of the addresses are aliases,\ni.e. they point to the same email inbox,\nchances are high that only one copy of the email is delivered to that inbox.</p>\n<h2 id=\"example-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-problem\">Example problem</a></h2>\n<ol>\n<li>I had five test users and one email alias per test user; e.g.:\n<ul>\n<li>Test user 1: <code>foo@example.com</code></li>\n<li>Test user 2: <code>foo+test-user-2@example.com</code></li>\n<li>Test user 3: <code>foo+test-user-3@example.com</code></li>\n<li>Test user 4: <code>custom-alias-1@example.com</code></li>\n<li>Test user 5: <code>custom-alias-2@example.com</code></li>\n<li>All five email addresses pointed to the same email inbox: <code>foo@example.com</code></li>\n</ul>\n</li>\n<li>I sent a single email to those five addresses.</li>\n<li><strong>I expected to receive five copies of the email to my email inbox, but I received only one copy.</strong>\n<ul>\n<li>In some test runs I sometimes received two or maybe even three emails,\nbut never all five.</li>\n</ul>\n</li>\n</ol>\n<h3 id=\"test-users-vs-real-users\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#test-users-vs-real-users\">Test users vs real users</a></h3>\n<p>In my case the users were test users,\nmaking testing email delivery difficult\nbecause only some test users received the emails.</p>\n<p>But the problem would affect real users too.\nExample:</p>\n<ul>\n<li>Somebody has registered to a service multiple times,\neach time using a different email alias.</li>\n<li>The service sends an email to all its users.</li>\n<li>That somebody should receive as many emails as they have users,\nbut (if this problem exists in that service) they might get only one email.</li>\n</ul>\n<h3 id=\"tangent-sendgrid\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#tangent-sendgrid\">Tangent: SendGrid</a></h3>\n<p>I used the <a href=\"https://github.com/sendgrid/sendgrid-nodejs/blob/v7.1.1/docs/use-cases/single-email-multiple-recipients.md\" class=\"link link-external\"><code>sendMultiple()</code> method of the SendGrid Node.js API</a>,\nbut that's tangential:</p>\n<ul>\n<li>This isn't a JavaScript-specific problem.</li>\n<li>This isn't a SendGrid-specific problem.</li>\n<li><strong>This is a general problem of delivering multiple copies of an email to multiple email aliases.</strong>\n<ul>\n<li>The same problem would happen when sending a single email to multiple email aliases non-programmatically, e.g. directly from Gmail.</li>\n</ul>\n</li>\n</ul>\n<p>Still, I wanted to mention this because at first I thought that the problem was in SendGrid.</p>\n<p>SendGrid's Activity Feed displayed the &quot;Delivered&quot; status for all emails,\nmeaning that the receiving mail servers had successfully received the emails.\nIndeed, the problem wasn't that SendGrid failed to send all the emails (it didn't fail);\nthe problem was that the receiving mail servers failed to deliver the duplicate emails to the inboxes.</p>\n<h2 id=\"the-root-cause-identical-message-ids\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-root-cause-identical-message-ids\">The root cause: identical <code>Message-Id</code>s</a></h2>\n<ul>\n<li>\n<p>Every email has a unique <code>Message-Id</code>.</p>\n<ul>\n<li>\n<p>I asked Fastmail Support &quot;Is the <code>Message-Id</code> always created/determined by the sender?&quot;\nand they replied:</p>\n<blockquote>\n<p>Yes, that's correct.\nHowever, if the sender doesn't specify one, due to a bug, we will create one during email delivery.</p>\n</blockquote>\n</li>\n</ul>\n</li>\n<li>\n<p>All emails sent in a single batch share the same <code>Message-Id</code>.</p>\n</li>\n<li>\n<p><strong>Most mail servers silently discard duplicate emails (based on <code>Message-Id</code>).</strong>\n(But there's a precondition; keep on reading.)</p>\n<ul>\n<li>\n<p>Based on my testing, this is true for at least these mail servers/providers:</p>\n<ul>\n<li>Exchange / Outlook</li>\n<li>Fastmail</li>\n<li>Gmail / Google Workspace (formerly G Suite)</li>\n</ul>\n</li>\n<li>\n<p>For example,\n<a href=\"https://www.fastmail.help/hc/en-us/articles/360058753614-Why-messages-bounce-back\" class=\"link link-external\">Fastmail's <em>Why messages bounce back</em> page</a>\nstates the following:</p>\n<blockquote>\n<p>Most messages have a unique identifier called the message ID.\nWhen a message arrives at an account, the message ID is noted.\nIf a second message with the same ID arrives, it is ignored and silently discarded.\nThis is used to stop infinite mail loops overloading your account or our system.\nHowever, it can result in odd behavior when the same message arrives from different sources, as only the first one will actually be delivered.</p>\n</blockquote>\n<p>Note the last sentence:</p>\n<blockquote>\n<p>However, it can result in odd behavior when the same message arrives from different sources, as only the first one will actually be delivered.</p>\n</blockquote>\n<p>Replacing &quot;from different sources&quot; with &quot;to multiple email aliases&quot; would keep the sentence correct and describe my problem better:</p>\n<blockquote>\n<p>However, it can result in odd behavior when the same message arrives <em>to multiple email aliases</em>, as only the first one will actually be delivered.</p>\n</blockquote>\n</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h2>\n<p>The solution is to <strong>send the emails sequentially (one at a time), e.g. at 1-second intervals</strong>.</p>\n<p>One way would be to make one API request per recipient,\nbut that would be slow and inefficient because there would be as many API requests as recipients.\n(Then each email would probably have unique <code>Message-Id</code>,\nwhich alone would fix the problem.)</p>\n<p>Eventually I found a better way (I'm using SendGrid as mentioned above):\n<strong>specifying different <a href=\"https://github.com/sendgrid/sendgrid-nodejs/blob/v7.1.1/docs/use-cases/scheduled-send.md\" class=\"link link-external\"><code>sendAt</code> values</a> for each recipient</strong>\nmakes SendGrid send the emails at different times,\nbut allows making as few API requests as possible.</p>\n<p>Curiously, the <code>Message-Id</code> is still the same in every email,\nbut this still fixes the problem!\nThat's because the emails don't arrive simultaneously.\nIn other words (this is the precondition mentioned above):</p>\n<p><strong>Most mail servers seem to <em>not</em> discard duplicate emails (based on <code>Message-Id</code>) if the emails arrive at different times (not simultaneously).</strong></p>\n<p>I have successfully tested this with:</p>\n<ul>\n<li>Exchange / Outlook</li>\n<li>Fastmail</li>\n<li>Gmail / Google Workspace (formerly G Suite)</li>\n</ul>\n<p>Fastmail Support confirmed this for their part:</p>\n<blockquote>\n<p>So it seems we ignore duplicate emails delivered in a very short period (e.g. within a second).\nThese emails were delivered within a second apart which is why they were not filtered as duplicates.\nThis is expected behavior.</p>\n</blockquote>\n<h3 id=\"code\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#code\">Code</a></h3>\n<p>Using SendGrid:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> emails <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>\n  <span class=\"token string\">'foo@example.com'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string\">'foo+test-user-2@example.com'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string\">'foo+test-user-3@example.com'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string\">'custom-alias-1@example.com'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string\">'custom-alias-2@example.com'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">]</span>\n\n<span class=\"token comment\">// SendGrid expects a UNIX timestamp in seconds</span>\n<span class=\"token keyword\">const</span> currentTimeInMilliSeconds <span class=\"token operator\">=</span> Date<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> currentTimeInSeconds <span class=\"token operator\">=</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">ceil</span><span class=\"token punctuation\">(</span>currentTimeInMilliSeconds <span class=\"token operator\">/</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span>\n\nsendGrid<span class=\"token punctuation\">.</span><span class=\"token function\">sendMultiple</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">personalizations</span><span class=\"token operator\">:</span> emails<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">email<span class=\"token punctuation\">,</span> index</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// Send the emails at 1-second intervals</span>\n    <span class=\"token comment\">// to ensure that delivery to email aliases works too.</span>\n    <span class=\"token comment\">// Mail servers silently discard duplicate emails</span>\n    <span class=\"token comment\">// that arrive to the same inbox at the same time,</span>\n    <span class=\"token comment\">// so multiple emails going to the same email inbox (via email aliases)</span>\n    <span class=\"token comment\">// need to be sent at different times.</span>\n    <span class=\"token comment\">// Also add a few seconds just to be safe,</span>\n    <span class=\"token comment\">// i.e. to avoid sending the first few emails simultaneously.</span>\n    <span class=\"token comment\">// More info: https://mtsknn.fi/blog/tricky-email-aliases/</span>\n    <span class=\"token keyword\">const</span> sendAt <span class=\"token operator\">=</span> currentTimeInSeconds <span class=\"token operator\">+</span> index <span class=\"token operator\">+</span> <span class=\"token number\">5</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">to</span><span class=\"token operator\">:</span> email<span class=\"token punctuation\">,</span>\n      sendAt<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">subject</span><span class=\"token operator\">:</span> <span class=\"token string\">'Test email'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">html</span><span class=\"token operator\">:</span> <span class=\"token string\">'&lt;p>Email body&lt;/p>'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h3 id=\"caveat\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#caveat\">Caveat</a></h3>\n<p>This fix/hack will make some recipients receive the email after a small delay.</p>\n<p>If you have 1,000 recipients and send the emails at 1-second intervals,\nthe last recipient will receive the email after about 1,000 seconds ≈ 17 minutes.\nIn my case that's totally OK.</p>\n<p>If that's too long,\nyou could send the emails in smaller batches.</p>\n<p>The maximum is anyway 1,000 recipients per one SendGrid API request.\nFrom <a href=\"https://docs.sendgrid.com/for-developers/sending-email/personalizations\" class=\"link link-external\">SendGrid's <em>Personalizations</em> page</a>:</p>\n<blockquote>\n<p>You may not include more than 1000 personalizations per API request.\nIf you need to include more than 1000 personalizations, please divide these across multiple API requests.</p>\n</blockquote>\n<h3 id=\"tangent-24-hours\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#tangent-24-hours\">Tangent: −24 hours</a></h3>\n<p>I stated two things above:</p>\n<ul>\n<li>Most mail servers silently discard duplicate emails (based on <code>Message-Id</code>).</li>\n<li>Most mail servers seem to <em>not</em> discard duplicate emails (based on <code>Message-Id</code>)\nif the emails arrive at different times (not simultaneously).</li>\n</ul>\n<p>As a test,\nI scheduled the emails to be sent at 1-second intervals like before,\nbut also <strong>24 hours in the past</strong>.\nLike this:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">sendGrid<span class=\"token punctuation\">.</span><span class=\"token function\">sendMultiple</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">personalizations</span><span class=\"token operator\">:</span> emails<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">email<span class=\"token punctuation\">,</span> index</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ⚠️ Example, don't actually do this</span>\n    <span class=\"token keyword\">const</span> twentyFourHoursInSeconds <span class=\"token operator\">=</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span> <span class=\"token operator\">*</span> <span class=\"token number\">24</span>\n    <span class=\"token keyword\">const</span> sendAt <span class=\"token operator\">=</span> currentTimeInSeconds <span class=\"token operator\">+</span> index <span class=\"token operator\">-</span> twentyFourHoursInSeconds\n\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n      sendAt<span class=\"token punctuation\">,</span>\n      <span class=\"token literal-property property\">to</span><span class=\"token operator\">:</span> email<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>I successfully received all emails – confusing!</p>\n<p>I asked about this from Fastmail Support:</p>\n<blockquote>\n<p>The timestamps in the Date headers are a second apart like expected;\nbut the timestamps in the Received headers are exactly the same.</p>\n<p>Doesn't this mean that SendGrid sent the five emails at the same time?\nI would expect SendGrid to do that because the Date headers were 24 hours in the past,\nbut I can't know for sure.</p>\n<p>And don't the identical Received timestamps also mean that\nyou received all emails at the same time?</p>\n</blockquote>\n<p>Their reply:</p>\n<blockquote>\n<p>It's based on the received time at our end.\nLooking at the logs I can see that these emails came in almost within a second.</p>\n<ul>\n<li><code>2022-11-06T23:30:33.991668-05:00 mx2 (info) postfix-mx/cleanup[630908]: F1DBE6A017A: message-id=&lt;a7ZrAF9NQbeVa12gUVIbRd@geopod-ismtpd-3-1&gt;</code></li>\n<li><code>2022-11-06T23:30:33.992920-05:00 mx3 (info) postfix-mx/cleanup[725605]: F22DB1960153: message-id=&lt;a7ZrAF9NQbeVa12gUVIbRd@geopod-ismtpd-3-1&gt;</code></li>\n<li><code>2022-11-06T23:30:34.039558-05:00 mx1 (info) postfix-mx/cleanup[774312]: 097E023C00C6: message-id=&lt;a7ZrAF9NQbeVa12gUVIbRd@geopod-ismtpd-3-1&gt;</code></li>\n<li><code>2022-11-06T23:30:34.066426-05:00 mx3 (info) postfix-mx/cleanup[725548]: 100301960158: message-id=&lt;a7ZrAF9NQbeVa12gUVIbRd@geopod-ismtpd-3-1&gt;</code></li>\n<li><code>2022-11-06T23:30:34.291125-05:00 mx1 (info) postfix-mx/cleanup[774312]: 46EA223C0074: message-id=&lt;a7ZrAF9NQbeVa12gUVIbRd@geopod-ismtpd-3-1&gt;</code></li>\n</ul>\n<p>So, they were not discarded as duplicates.</p>\n</blockquote>\n<p>So, SendGrid sent the emails at slightly different times\neven though they were scheduled to be sent 24 hours ago.\nOr at least Fastmail received them at slightly different times.</p>\n<p>I wouldn't rely on this behavior,\nso <strong>I don't recommend this approach (scheduling the emails to be sent in the past)</strong>,\nbut this is interesting to know nonetheless.</p>\n<h3 id=\"non-solutions\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#non-solutions\">Non-solutions</a></h3>\n<p>I tried these two things that didn't work:</p>\n<ul>\n<li>\n<p><a href=\"https://github.com/sendgrid/sendgrid-nodejs/blob/v7.1.1/docs/use-cases/custom-headers.md\" class=\"link link-external\">Custom headers</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">sendgrid<span class=\"token punctuation\">.</span><span class=\"token function\">sendMultiple</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">personalizations</span><span class=\"token operator\">:</span> emails<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">email<span class=\"token punctuation\">,</span> index</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">to</span><span class=\"token operator\">:</span> email<span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">headers</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token string-property property\">'X-EmailBatchId'</span><span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>index <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n</li>\n<li>\n<p><a href=\"https://github.com/sendgrid/sendgrid-nodejs/blob/main/docs/use-cases/customization.md\" class=\"link link-external\">Custom subject per recipient</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">sendgrid<span class=\"token punctuation\">.</span><span class=\"token function\">sendMultiple</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">personalizations</span><span class=\"token operator\">:</span> emails<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">email<span class=\"token punctuation\">,</span> index</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">to</span><span class=\"token operator\">:</span> email<span class=\"token punctuation\">,</span>\n\n    <span class=\"token literal-property property\">subject</span><span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Email </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>index <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">// or</span>\n    <span class=\"token literal-property property\">dynamicTemplateData</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">subject</span><span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Email </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>index <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n</li>\n</ul>\n<h2 id=\"rant-sendgrid-support-was-meh\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#rant-sendgrid-support-was-meh\">Rant: SendGrid Support was meh</a></h2>\n<p>SendGrid Support couldn't help me solve this problem.</p>\n<p>That's kind of okay because this is quite an obscure thing and not specific to SendGrid;\non the other hand, I'd expect an email delivery service provider to have great knowledge about even the most obscure things related to delivering emails.</p>\n<p>What's worse is that\none of their &quot;Senior Technical Support Engineers&quot; even said something factually incorrect:</p>\n<blockquote>\n<p>I believe the root of the issue here is that multiple recipients require multiple requests, <em>unless</em> you're including them as CC or BCC.\nAt some point in 2021, our system began no longer accepting multiple &quot;TO&quot; recipients within one request.\nWhereas previously, you could have 1 request with multiple recipients listed in the TO portion.\nNow, to send 1 email to 10 recipients individually, this requires 10 individual requests.</p>\n<p>I suggest that you and your team look into adjusting your system's workflow so that such situations result in multiple requests, rather than using &quot;sendmultiple&quot; within 1 request.</p>\n</blockquote>\n<p>I don't understand that reply\nas it's simply false that you can't send an email to multiple recipients with a single request.</p>\n<p>The SendGrid docs have a section\n<a href=\"https://docs.sendgrid.com/for-developers/sending-email/personalizations#sending-the-same-email-to-multiple-recipients\" class=\"link link-external\"><em>Sending the same email to multiple recipients</em></a>.\nAt the top of the page,\nit's mentioned that you can send an email to max 1,000 recipients with a single request.\nThat's much better and faster than doing 1,000 individual requests.</p>\n<p>Plus I had already succeeded at sending an email to multiple recipients with a single request;\nI was just having problems with email aliases / duplicate emails.</p>\n<h2 id=\"praise-fastmail-support-is-great\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#praise-fastmail-support-is-great\">Praise: Fastmail Support is great</a></h2>\n<p>Fastmail Support was very helpful in helping me solve and understand the problem.</p>\n<p>They have been helpful in the past too,\nwhen I have had email problems.</p>\n","summary":"If you send a single email to a bunch of email addresses,\nand some of the addresses are aliases,\ni.e. they point to the same email inbox,\nchances are high that only one copy of the email is delivered to that inbox.","date_published":"2023-04-21T12:00:00+03:00","tags":["JavaScript","SendGrid"]},{"id":"https://mtsknn.fi/blog/mirror-of-erised/","url":"https://mtsknn.fi/blog/mirror-of-erised/","title":"Harry Potter, the Mirror of Erised, and mindless internet surfing","content_html":"<p>Mindlessly surfing the internet\nis like staring at the Mirror of Erised from the Harry Potter books.\n(Minor Harry Potter spoilers ahead.)</p>\n<h2 id=\"mindless-surfing-vs-the-mirror-of-erised\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#mindless-surfing-vs-the-mirror-of-erised\">Mindless surfing vs the Mirror of Erised</a></h2>\n<p><a href=\"https://hanki.dev/\" class=\"link link-external\">My bro</a> shared me <a href=\"https://old.reddit.com/r/nosurf/comments/11p5mzo/mindless_surfing_is_like_the_mirror_of_erised_in/\" class=\"link link-external\">a Reddit post on /r/nosurf</a> the other day.</p>\n<p>Here's the full post (with a few typos fixed):</p>\n<blockquote>\n<p><strong>Mindless surfing is like the Mirror of Erised in Harry Potter</strong></p>\n<p>For those of you who haven't read Harry Potter,\nthe Mirror of Erised is a mirror which shows the deepest desires of a person.\nIn the story, Harry finds the mirror and sees his parents in it.\nHe keeps returning every day until Dumbledore gently tells him to stop doing so.</p>\n<blockquote>\n<p>This mirror will give us neither knowledge or truth.\nMen have wasted away before it, entranced by what they have seen, or been driven mad, not knowing if what it shows is real or even possible.\nIt does not do well to dwell on dreams and forget to live.</p>\n</blockquote>\n<p>This is a great parallel to mindless internet surfing.\nWe watch our deepest desires on the internet like being rich, falling in love, living a certain lifestyle or even being in another country.\nI've spent a lot of time browsing nice apartment pics, vlogs, or even street view of nice cities where I wanna live, not because of curiosity or exploration but because of a desire to see my dream.\nThis is actually preventing me from living fully and actually working to achieve my dreams in the first place.</p>\n<p>I just found it interesting how similar the two of these are.</p>\n</blockquote>\n<h2 id=\"longer-harry-potter-quote\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#longer-harry-potter-quote\">Longer Harry Potter quote</a></h2>\n<p>I recently started reading the Harry Potter books for the first time.</p>\n<p>The first book,\n<em>Harry Potter and the Philosopher's Stone</em>,\nhas 332 pages (Bloomsbury, 2014 edition),\nbut I read it in just 5 days.\nIt was that good!</p>\n<p>The first book also introduces the Mirror of Erised.\nFrom pages 229–230:</p>\n<blockquote>\n<p>'Let me explain.\nThe happiest man on earth would be able to use the Mirror of Erised like a normal mirror,\nthat is, he would look into it and see himself exactly as he is.\nDoes that help?'</p>\n<p>Harry thought.\nThen he said slowly,\n'It shows us what we want … whatever we want …'</p>\n<p>'Yes and no', said Dumbledore quietly.\n'It shows us nothing more or less than the deepest, most desperate desire of our hearts.\nYou, who have never known your family, see them standing around you.\nRonald Weasley, who has always been overshadowed by his brothers, sees himself standing alone, the best of all of them.\nHowever, this mirror will give us neither knowledge or truth.\nMen have wasted away before it, entranced by what they have seen, or been driven mad, not knowing if what it shows is real or even possible.</p>\n<p>'The Mirror will be moved to a new home tomorrow, Harry, and I ask you not to go looking for it again.\nIf you ever <em>do</em> run across it, you will now be prepared.\nIt does not do to dwell on dreams and forget to live, remember that.\nNow, why don't you put that admirable Cloak back on and get off to bed?'</p>\n<p>Harry stood up.</p>\n<p>'Sir – Professor Dumbledore? Can I ask you something?'</p>\n<p>'Obviously, you've just done so,' Dumbledore smiled. 'You may ask me one more thing, however.'</p>\n<p>'What do you see when you look in the Mirror?'</p>\n<p>'I? I see myself holding a pair of thick, woollen socks.'</p>\n<p>Harry stared.</p>\n<p>'One can never have enough socks,' said Dumbledore.\n'Another Christmas has come and gone and I didn't get a single pair.\nPeople will insist on giving me books.'</p>\n<p>It was only when he was back in bed that it struck Harry that Dumbledore might not have been quite truthful.\nBut then, he thought, as he shoved Scabbers off his pillow, it had been quite a personal question.</p>\n</blockquote>\n","summary":"Mindlessly surfing the internet\nis like staring at the Mirror of Erised from the Harry Potter books.\n(Minor Harry Potter spoilers ahead.)","date_published":"2023-04-14T12:00:00+03:00","tags":["Books","NoSurf"]},{"id":"https://mtsknn.fi/blog/agile-process-visualized/","url":"https://mtsknn.fi/blog/agile-process-visualized/","title":"Video notes: Agile Product Ownership in a Nutshell","content_html":"<p>This 16-minute video by Henrik Kniberg\nis a great overview of the agile software development process,\nbut also contains lots of smaller great insights.</p>\n<h2 id=\"the-video\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-video\">The video</a></h2>\n<p><a href=\"https://www.youtube.com/watch?v=502ILHjX9EE\" class=\"link link-external\">Watch <em>Agile Product Ownership in a Nutshell</em> (15:51) on YouTube.</a></p>\n<p>I found the video\nvia <a href=\"https://critter.blog/2023/04/06/pull-vs-push-visualized/\" class=\"link link-external\">Critter's blog post <em>Pull methodology, flow time, and WIP limits: visualized</em></a>.</p>\n<h2 id=\"top-3-moments\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#top-3-moments\">Top 3 moments</a></h2>\n<p>The 16-minute video is packed with great insights.\nI guess different things would stand out when rewatching the video later;\nthis time these three things resonated with me the most.</p>\n<h3 id=\"knowledge-focus-vs-customer-value-focus-812-916\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#knowledge-focus-vs-customer-value-focus-812-916\">Knowledge focus vs customer value focus (8:12–9:16)</a></h3>\n<p><img src=\"./value-graph.png\" alt=\"\"></p>\n<blockquote>\n<p>Knowledge can be seen as the opposite of risk, so when uncertainty is high, our focus is [on] knowledge acquisition; we focus on things like UI prototypes and technical spikes/experiments. Maybe not too exciting for the customers [(stakeholders)], but still valuable because we are reducing risk. From a customer value perspective, the curve [of customer value (y-axis) and time (x-axis)] looks [flat] in the beginning.</p>\n<p>As uncertainty is reduced, we gradually focus more and more on customer value; we know what we are going to build and how, so just do it. And by doing the highest-value [user] stories first, we get this nice steep value curve.</p>\n<p>And then gradually the value curve starts flattening out. We have built the most important stuff, and now we are just adding the bonus features; the toppings of an ice cream. This is a nice place to be [in] because at any point [the Product Owner] and the team may decide to &quot;trim the tail,&quot; to cut right here and move on to another more important project. Or maybe start a whole new feature area within the same product. That is business agility.</p>\n<p>So when I talk about value [of user stories], I actually mean knowledge value + customer value. And we need to continuously find the trade-off between these two.</p>\n</blockquote>\n<h3 id=\"correct-vs-good-vs-fast-939-1042\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#correct-vs-good-vs-fast-939-1042\">Correct vs good vs fast (9:39–10:42)</a></h3>\n<p><img src=\"./correct-good-fast.png\" alt=\"\"></p>\n<blockquote>\n<p>Should we focus on:</p>\n<ul>\n<li>building the right thing</li>\n<li>or building the thing right</li>\n<li>or perhaps building it fast?</li>\n</ul>\n<p>Ideally we want all three, but it's hard to find the balance.</p>\n<p>[...]</p>\n<ul>\n<li>Product owners tend to focus on building the right thing.</li>\n<li>Development teams tend to focus on building the thing right.</li>\n<li>Scrum masters or agile coaches tend to focus on shortening the feedback loop.</li>\n</ul>\n<p><strong>Speed is actually worth emphasizing</strong> because a short feedback loop will accelerate learning, so you'll more quickly learn what the right thing is and how to build it right.</p>\n<p>However, all three aspects are important, so keep trying to find the balance.</p>\n</blockquote>\n<h3 id=\"reducing-scope-vs-extending-time-1315-1400\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#reducing-scope-vs-extending-time-1315-1400\">Reducing scope vs extending time (13:15–14:00)</a></h3>\n<p><img src=\"./cut-scope-vs-extend-time.png\" alt=\"\"></p>\n<blockquote>\n<p>Suppose a stakeholder says: &quot;Can we get these features by Christmas?&quot; Now, that's a fixed time, fixed scope question.</p>\n<p>Looking at trend lines, [the Product Owner] says: &quot;Nope, sorry, ain't gonna happen,&quot; followed by, &quot;here's how much we can get done by Christmas&quot; or &quot;here's how much more time we would need to get everything done.&quot;</p>\n<p><strong>It's generally better to reduce scope than to extend time,</strong> because if we reduce scope first, we still have the option to extend the time later and add the rest of the [user] stories.</p>\n<p>Vice versa doesn't work because, darn it, we can't turn the clock backwards. [...]</p>\n<p>So, [the Product Owner] puts it this way: &quot;We could deliver something [soon] and the rest later, or we could deliver nothing [soon] and the rest later; which do you prefer?&quot;</p>\n</blockquote>\n","summary":"This 16-minute video by Henrik Kniberg\nis a great overview of the agile software development process,\nbut also contains lots of smaller great insights.","date_published":"2023-04-11T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/atomic-habits/","url":"https://mtsknn.fi/blog/atomic-habits/","title":"Book notes: Atomic Habits","content_html":"<p>Tiny good things accumulate into great things.\nTiny bad things accumulate into horrible things.\nThis book tells you how to do more of those good things\nand less of those bad things.</p>\n<h2 id=\"about-the-book\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#about-the-book\">About the book</a></h2>\n<p>People do things out of habit all the time.</p>\n<p><em>Atomic Habits</em> tells you how to make good habits easy and effortless,\nand how to make bad habits difficult and unattractive.</p>\n<p><a href=\"https://jamesclear.com/atomic-habits\" class=\"link link-external\">Read more about <em>Atomic Habits</em> on James Clear's website</a></p>\n<h2 id=\"thoughts\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#thoughts\">Thoughts</a></h2>\n<p>I read this book in January 2023.\nThis was the first book I read in a very long time (a few years maybe).</p>\n<p>The book was great:</p>\n<ul>\n<li>Interesting topic</li>\n<li>Well written; not boring at all</li>\n<li>Many examples and anecdotes</li>\n<li>A good balance between theory and practical tips &amp; tricks</li>\n</ul>\n<p>I found the book to be a nice general overview of human behavior:\nwhy people do certain things (good or bad)\nand why people don't do certain things (good or bad).\nSo the book isn't just about productivity.</p>\n<h2 id=\"notes\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#notes\">Notes</a></h2>\n<p>These are the things that resonated with me the most.</p>\n<p>I wrote these originally on post-it notes in January 2023.\nNow, almost 3 months later, I'm having difficulties understanding some of the post-its,\nso the notes below are partly confusing. 🙈</p>\n<h3 id=\"how-to-do-a-habit-more\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#how-to-do-a-habit-more\">How to do a habit more</a></h3>\n<p>Make it:</p>\n<ol>\n<li>Obvious</li>\n<li>Attractive</li>\n<li>Easy</li>\n<li>Satisfying</li>\n</ol>\n<p>(Note to self: examples would be nice...)</p>\n<h3 id=\"how-to-ditch-a-bad-habit\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#how-to-ditch-a-bad-habit\">How to ditch a bad habit</a></h3>\n<p>Make it:</p>\n<ol>\n<li>Invisible (out of sight, out of mind)</li>\n<li>Unattractive</li>\n<li>Difficult</li>\n<li>Unsatisfying</li>\n</ol>\n<h3 id=\"the-1percent-rule\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-1percent-rule\">The 1% rule</a></h3>\n<ul>\n<li>Getting 1% better at something (or improving something by 1%) every day for one year\nyields great dividends in the long run\n<ul>\n<li>1.01³⁶⁵ ≈ 37.78</li>\n<li>Progress is slow but exponential/compounding</li>\n</ul>\n</li>\n<li>Conversely: regressing 1% at something every day for one year destroys the original value\n<ul>\n<li>0.99³⁶⁵ ≈ 0.0255</li>\n<li>So bad habits, stress and negative thoughts compound too!</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"focus-on-processes-not-goals\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#focus-on-processes-not-goals\">Focus on processes, not goals</a></h3>\n<ul>\n<li>Focusing on a process is living in the moment;\nfocusing on a goal is living in the future</li>\n<li>Enjoy what you are doing; treat goals as a bonus</li>\n<li>Achieving a goal is a momentary change\n<ul>\n<li>Focusing on a goal can feel discouraging and like nothing is happening</li>\n<li>Missing a goal can feel like failure even if you have made good progress</li>\n<li>Example: Melting an ice cube\n<ul>\n<li>Rising the temperature from −30 °C to −1 °C\nlooks like it doesn't do anything to an ice cube</li>\n<li>Finally rising the temperature one or two degrees more melts the ice cube</li>\n<li>The &quot;trick&quot; wasn't to rise the temperature only one or two degrees –\nit was to rise it all those 30+ degrees</li>\n</ul>\n</li>\n<li>Example: Stonemasonry\n<ul>\n<li>A stonemason might hit the same spot 99 times,\nand it might look the hitting doesn't do anything</li>\n<li>Finally the 100th hit makes the rock to chip off</li>\n<li>All 100 hits were required even though 99% of them looked like they did nothing</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"1-task-per-day\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#1-task-per-day\">1 task per day</a></h3>\n<ul>\n<li>Do one task per day, and you have done 365 tasks at the end of the year.</li>\n<li>Otherwise you'll have 365+ pending tasks at the end of the year!</li>\n</ul>\n<p>(I don't remember if the book had this kind of example\nor whether this was my own though based on something else. 🤔)</p>\n<h3 id=\"become-your-ideal-person\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#become-your-ideal-person\">Become your ideal person</a></h3>\n<ol>\n<li>Decide who you want to be(come).</li>\n<li>Prove it to yourself with small wins.</li>\n</ol>\n<p>→ It's not about the goals but the system/process and identities.</p>\n<ul>\n<li>Habits shape identities.</li>\n<li>Identities cause habits.</li>\n<li>Family &amp; friends, tribe (neighborhood, nationality etc.) and the powerful</li>\n</ul>\n<p>(I'm not sure what the notes in this chapter mean. 🫣 Shitty notes.)</p>\n<h3 id=\"habits-free-up-mental-juice\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#habits-free-up-mental-juice\">Habits free up mental juice</a></h3>\n<ul>\n<li>Build habits to free up mental space (conscious brain juice) for other things.</li>\n<li>For example: it's great to not have to always think about what to eat or when to exercise etc.</li>\n</ul>\n<h3 id=\"pointing-and-calling\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#pointing-and-calling\">Pointing-and-Calling</a></h3>\n<ul>\n<li>Pointing at things makes your thoughts more conscious.</li>\n<li>Saying things aloud makes your thoughts more conscious.</li>\n<li>Combine pointing and saying aloud for the best effect (&quot;Pointing-and-Calling&quot;).</li>\n</ul>\n<h3 id=\"aside-baseball-bats\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#aside-baseball-bats\">Aside: baseball bats</a></h3>\n<p>In the intro chapter,\nJames Clear tells how he was once hit in the face by a flying baseball bat.</p>\n<p>His skull fractured and brains swelled etc.,\ncausing him to almost die.</p>\n<p>That's completely different what some movies and TV series portray.\nSometimes a guy is hit dozens of times with a (metallic!) baseball bat,\neven to the head,\nand all it does is make the guy groan a little.\nRidiculous.\n(I had been shaking my head at this stupidity before reading the book;\nJames's anecdote just confirmed my hunch that even a single hit could be fatal.)</p>\n<h2 id=\"verdict\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#verdict\">Verdict</a></h2>\n<p><em>Atomic Habits</em> is a great book, go read it!</p>\n","summary":"Tiny good things accumulate into great things.\nTiny bad things accumulate into horrible things.\nThis book tells you how to do more of those good things\nand less of those bad things.","date_published":"2023-04-07T12:00:00+03:00","tags":["Books"]},{"id":"https://mtsknn.fi/blog/small-monitor-best/","url":"https://mtsknn.fi/blog/small-monitor-best/","title":"From a super ultrawide monitor to a regular 16:9 monitor","content_html":"<p>After 2.5 years of using a 120 cm wide super ultrawide computer monitor with a 32:9 aspect ratio,\nI'm back to a boring 64 cm wide 16:9 monitor.\nThe smaller monitor now feels better in every regard.</p>\n<h2 id=\"super-ultrawide\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#super-ultrawide\">Super ultrawide</a></h2>\n<p>I bought <a href=\"https://www.samsung.com/us/computing/monitors/gaming/49-crg9-dual-qhd-curved-qled-gaming-monitor-lc49rg90ssnxza/\" class=\"link link-external\">Samsung CRG9</a>\nin July 2020 for 950 € (sale price).\nSome specs:</p>\n<ul>\n<li>49&quot;, 120 cm wide</li>\n<li>5120×1440 pixels (Dual QHD)</li>\n<li>120 Hz</li>\n<li>Curved VA panel</li>\n</ul>\n<h3 id=\"nice-its-wide\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#nice-its-wide\">Nice: it's wide</a></h3>\n<p>I wanted to try a super ultrawide monitor\nbecause the wider the screen, the more windows can fit on it side by side.</p>\n<p>I thought that would be useful,\nand it kind of is.\nHaving VS Code, Terminal and Firefox side by side can be nifty\nwhen coding web apps.</p>\n<h3 id=\"drawback-distractions\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#drawback-distractions\">Drawback: distractions</a></h3>\n<p>On the other hand,\nhaving more than one thing open at the same time can be distracting.\nAt least subconsciously.</p>\n<p>I hadn't considered this until I read\nAlexey Guzey's blog post <a href=\"https://guzey.com/2022-lessons/\" class=\"link link-external\"><em>My 2022 self (I don't know them) was very wrong about meditation, huge monitors, and... sleep</em></a>.</p>\n<p><a href=\"https://guzey.com/2022-lessons/#the-more-the-bigger-monitors-the-better---a-single-16-laptop-monitor-is-perfect\" class=\"link link-external\">In chapter 4, Alexey tells</a>\nthat he changed from his 4-monitor setup (49&quot; + 34&quot; + 24&quot; + 16&quot;)\nto using just his 16&quot; MacBook.</p>\n<blockquote>\n<p>I'm actually distracted by [having] my task list, calendar, notes, etc. always visible.</p>\n<p>[Now] I work on my 16&quot; MacBook and do one thing at a time.</p>\n</blockquote>\n<p>So: one or max two windows open at a time = more focus.</p>\n<h3 id=\"other-drawbacks\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#other-drawbacks\">Other drawbacks</a></h3>\n<ul>\n<li>The super ultrawide monitor is annoyingly wide,\nso I had to turn my head quite a bit to look at the sides.</li>\n<li>The monitor is very heavy and thus difficult to move around.</li>\n<li><a href=\"/blog/my-new-microphone-rode-procaster/\" class=\"link\">My microphone</a>\nblocked about a third of the monitor when in voice calls.</li>\n</ul>\n<p>If you play video games (I play only rarely):</p>\n<ul>\n<li>Some games put certain UI elements in the corners,\nso they are way too far away from the center where the action usually happens.\nE.g. the minimap in Battlefield 4.</li>\n<li>In some games dual QHD can theoretically be more taxing for the GPU than 4K\neven though dual QHD has less total pixels than 4K (7 372 800 vs 8 294 400).\nThat's because in many games there's more heavy stuff to\nrender horizontally (e.g. other players etc.)\nthan vertically (e.g. mostly sky and clouds).</li>\n</ul>\n<h2 id=\"testing-smaller-resolutions-with-a-software\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#testing-smaller-resolutions-with-a-software\">Testing smaller resolutions with a software</a></h2>\n<p>Before changing to a smaller monitor,\nI &quot;converted&quot; my super ultrawide monitor to a smaller monitor.</p>\n<p>I found <a href=\"https://www.madrau.com/\" class=\"link link-external\">SwitchResX for Mac</a> to be great:\nit allowed me to apply black bars to the sides of my monitor.</p>\n<p>First I tried regular ultrawide resolution (3440×1440, WQHD, 43:18) for a couple of days.\nDefinitely better!\nLess looking too far to the sides.</p>\n<p>The regular ultrawide resolution started to quickly feel still too wide.\nThen I reduced the resolution to 2560×1440 (2K, 16:9).\nPerfect aspect ratio!</p>\n<p>(16:10 could also work,\nbut there aren't many external monitors available with that aspect ratio.)</p>\n<h2 id=\"back-to-169\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#back-to-169\">Back to 16:9</a></h2>\n<p>I had bought <a href=\"https://www.benq.eu/en-eu/business/monitor/bl2711u.html\" class=\"link link-external\">BenQ BL2711U</a> in November 2017\nbut had never sold it,\nso I already had a great monitor lying around.\nThe perks of procrastination.</p>\n<p>Some specs:</p>\n<ul>\n<li>27&quot;, 64 cm wide</li>\n<li>3840×2160 pixels (4K)</li>\n<li>60 Hz</li>\n<li>Flat (non-curved) IPS panel</li>\n</ul>\n<p>Now I have less horizontal pixels (3840 vs 5120)\nbut that's okay because\nit's why I wanted to change to a smaller monitor in the first place.</p>\n<p>Two windows side by side, both taking 50% width, feels actually quite good now.\nOn the other hand,\nplacing two windows side by side on an ultrawide makes both windows a bit too wide.</p>\n<p>As a bonus I have more vertical pixels (2160 vs 1440)\nand a nice IPS panel.</p>\n<h3 id=\"27-vs-32\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#27-vs-32\">27&quot; vs 32&quot;</a></h3>\n<p>This 27&quot; monitor feels perfect.\nNot too small, not too big.</p>\n<p>A 32&quot; monitor would probably be a little too big for my taste.\nI would again have to start turning my head around too much\nwhen looking at the sides.\nAnd either the bottom side of the screen would be too low,\nor the top side would be too high.</p>\n<h2 id=\"meme\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#meme\">Meme</a></h2>\n<p>I'll conclude this post with this nice meme\n(idea by <a href=\"https://hanki.dev/\" class=\"link link-external\">my bro</a>).</p>\n<p><img src=\"./meme.jpg\" alt=\"The &quot;IQ Bell Curve / Midwit&quot; meme with the low IQ and high IQ persons saying &quot;I use only one monitor&quot; and the midwit person saying &quot;Need ultrawide or 3+ monitors!&quot;\"></p>\n","summary":"After 2.5 years of using a 120 cm wide super ultrawide computer monitor with a 32:9 aspect ratio,\nI'm back to a boring 64 cm wide 16:9 monitor.\nThe smaller monitor now feels better in every regard.","date_published":"2023-03-23T12:00:00+03:00","date_modified":"2023-05-12T12:00:00+03:00","tags":["Battlestation"]},{"id":"https://mtsknn.fi/blog/bye-weekly-log/","url":"https://mtsknn.fi/blog/bye-weekly-log/","title":"I wrote a weekly log for half a year and it was stressful","content_html":"<p>&quot;Weekly log&quot; basically meaning weekly blog posts with a specific structure.\nToday I removed the whole Weekly log.</p>\n<h2 id=\"why-i-started-a-weekly-log\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-i-started-a-weekly-log\">Why I started a weekly log</a></h2>\n<p>I started the weekly log\nto reduce stress:\nby publishing short weekly log entries,\nI wouldn't need to stress so much about my blog.</p>\n<p>Writing blog posts takes time,\nand I wanted to publish something regularly.</p>\n<p>And I did publish regularly:\nI published the first entry on the first week of 2021\nand then a new entry each week for 6 months\nfor a total of 26 entries.</p>\n<h2 id=\"why-i-ditched-it\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-i-ditched-it\">Why I ditched it</a></h2>\n<p>So I wanted to reduce stress,\nbut here's what actually happened:\nI had two things to maintain;\nthe blog and the weekly log.</p>\n<p>Or actually three,\nbecause <a href=\"/blog/goodbye-cookbook-hello-more-blog-posts/\" class=\"link\">this site used to also have a Cookbook section with &quot;technical recipes.&quot;</a></p>\n<p>Publishing every week was nice for a while,\nbut quickly started to feel like work.\nHaving an obligation like that was stressful.</p>\n<p>Plus I didn't manage to write short and quick weekly log entries,\nso it wasn't much easier or more enjoyable than writing blog posts.</p>\n<h2 id=\"how-i-ditched-it\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#how-i-ditched-it\">How I ditched it</a></h2>\n<p>I moved the best bits to individual blog posts\nand deleted the rest (unnecessary ramblings etc.).\nI did this slowly, so looks like it took me almost two years.</p>\n<h2 id=\"other-issues\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#other-issues\">Other issues</a></h2>\n<h3 id=\"bad-discoverability-and-seo\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#bad-discoverability-and-seo\">Bad discoverability and SEO</a></h3>\n<p>(SEO = search engine optimization)</p>\n<p>The weekly log entries were on a separate &quot;Weekly log&quot; page.\nSomeone going through my blog posts might miss entirely that the &quot;Weekly log&quot; page has more blog-like stuff.</p>\n<p>Opening a weekly log entry was not a nice experience either\nbecause the page was long and had lots of mixed content to go through.</p>\n<p>The weekly log entries appeared on Google with names like &quot;22nd week of 2021 – Weekly log.&quot;\nSuch titles aren't very clear nor inviting.</p>\n<p>So it wasn't easy to discover what topics the weekly log entries actually cover.</p>\n<h3 id=\"too-rigid-structure\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#too-rigid-structure\">Too rigid structure</a></h3>\n<p>My weekly log was mainly inspired by <a href=\"https://samselikoff.com/work-journal\" class=\"link link-external\">Sam Selikoff's Work journal</a>\nin which he publishes weekly bullet points\nabout work-related stuff, learnings and interesting stuff.</p>\n<p>Piggybacking Sam's Work journal,\nI structured my weekly log entries around these four headings:</p>\n<ul>\n<li>👨‍💼 Work</li>\n<li>👨‍🚀 Personal</li>\n<li>👨‍🎓 Learnings</li>\n<li>🕵️‍♂️ Cool stuff</li>\n</ul>\n<p>That's sort of nice,\nbut also sort of rigid.\nI didn't have anything to say in each four category each week,\nso I either said something pointless anyway\nor felt bad that the section was empty.</p>\n<h3 id=\"too-much-effort\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#too-much-effort\">Too much effort</a></h3>\n<p>I should have written only bullet points (and headings),\nbut I wrote full paragraphs, and <em>many</em> of them.\nI put way too much time and effort into something I originally wanted to be a chill little side quest.</p>\n<p>The reading experience could have been better too\nhad I written more shortly.\nSam Selikoff's Work journal (linked above) is nice to read because it's concise:\nit has only headings and bullet points,\nand most bullet points are short (even only one sentence).\nWho wants to read too lengthy ramblings? Yeah, like this very text here.</p>\n<p>Also, the posts could have been regular blog posts with a &quot;Weekly log&quot; tag.\nWhy complicate things?</p>\n<h2 id=\"lessons-learned\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#lessons-learned\">Lessons learned</a></h2>\n<p>Next time I'll think twice before committing to an arbitrary schedule.\nPublishing something regularly is nice,\nbut not if it starts to feel like work.</p>\n<p>Next time I'll also try to keep things simple and flexible.\nMy weekly log entries could have been regular blog posts with only headings and bullet points.</p>\n","summary":"\"Weekly log\" basically meaning weekly blog posts with a specific structure.\nToday I removed the whole Weekly log.","date_published":"2023-03-17T12:00:00+03:00","tags":["Blogging"]},{"id":"https://mtsknn.fi/blog/from-windows-to-mac/","url":"https://mtsknn.fi/blog/from-windows-to-mac/","title":"Switching to Mac after 20+ years of using Windows","content_html":"<p>I have used Windows PCs since I was a kid.\nA year ago (Jan 2022) I got my first MacBook.\nWhile Mac isn't perfect,\nit's so much better that I'm not going back to Windows.</p>\n<h2 id=\"why-i-switched-to-mac\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-i-switched-to-mac\">Why I switched to Mac</a></h2>\n<p>Three things:</p>\n<ol>\n<li>I had long had the impression that many developers use and like Macs,\nso I was intrigued to try it myself.</li>\n<li>Macs are relatively expensive\n(same amount of money gets you more horsepower if you buy a Windows PC,\nthough I'm not sure if M1/M2 Macs have changed the game),\nand I'm a cheapo.</li>\n<li>A year ago (Jan 2022) I switched jobs\nand could freely choose a work laptop (a great perk by the way),\nso I took the opportunity and chose a MacBook.</li>\n</ol>\n<h2 id=\"how-long-it-took-me-to-get-familiar-with-mac\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#how-long-it-took-me-to-get-familiar-with-mac\">How long it took me to get familiar with Mac</a></h2>\n<p>Only about three days:</p>\n<ul>\n<li>Thu: fetched my MacBook from the office</li>\n<li>Fri–Sun: got to know the MacBook, configured things, installed apps</li>\n<li>Mon: started my new consulting gig</li>\n</ul>\n<p>The first work days consisted mostly of introductions to the customer's systems etc.,\nbut even if the very first work day was full of coding,\nI feel I wouldn't have been any more productive had I used a Windows PC\n(because of the pros of Mac, listed below).</p>\n<p>It was a bit risky to switch from Windows to Mac with such a short time frame,\nbut it was worth it.</p>\n<h2 id=\"things-i-like-in-mac\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#things-i-like-in-mac\">Things I like in Mac</a></h2>\n<h3 id=\"unix\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#unix\">Unix</a></h3>\n<p>Mac is a Unix operating system, so:</p>\n<ul>\n<li>Terminal works great and is fast\n<ul>\n<li><a href=\"https://gitforwindows.org/\" class=\"link link-external\">Git Bash for Windows</a> is slow in comparison</li>\n<li><a href=\"https://learn.microsoft.com/en-us/windows/wsl/about\" class=\"link link-external\">WSL (Windows Subsystem for Linux)</a>\nfelt fragile and had performance issues last time I used it</li>\n</ul>\n</li>\n<li>Unix tools work out of the box\n<ul>\n<li><a href=\"https://ohmyz.sh/\" class=\"link link-external\">Oh My Zsh</a> gave me a nice Terminal experience very easily</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"fast\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#fast\">Fast</a></h3>\n<p>The hardware matters as well;\nI switched from Lenovo ThinkPad X1 Carbon Gen 7\nto MacBook Pro 16&quot; M1 Pro (32 GB).</p>\n<p>My MacBook is not as fast as I thought –\nI had read high praises from several sources,\nso my expectations were very high –\nbut it's still fast.</p>\n<p>Every time I use my Windows laptop\nI'm baffled how sluggish it is.\nSo compared to that,\nyes, my MacBook is very fast.</p>\n<h3 id=\"better-keyboard-shortcuts\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#better-keyboard-shortcuts\">Better keyboard shortcuts</a></h3>\n<p>Using the <kbd>Cmd</kbd> key (next to the space bar) with the left thumb\nis more ergonomic than reaching for the <kbd>Ctrl</kbd> key with the left pinky.</p>\n<h2 id=\"things-i-miss-from-windows\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#things-i-miss-from-windows\">Things I miss from Windows</a></h2>\n<h3 id=\"the-menu-keyboard-key\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-menu-keyboard-key\">The Menu keyboard key</a></h3>\n<p>The <a href=\"https://en.wikipedia.org/wiki/Menu_key\" class=\"link link-external\">Menu key</a>\nis essential for comfortable keyboard navigation across Windows:</p>\n<blockquote>\n<p>In computing, the <strong>menu key</strong> or <strong>application key</strong> (<kbd>≣ Menu</kbd>) is a key found on Microsoft Windows-oriented computer keyboards [...]. It is typically found on the right side of the keyboard between the right Windows logo key and the right control key [...].</p>\n<p>The key's primary function is to launch a context menu with the keyboard rather than with the usual right-mouse button. It can be used when the right-mouse button is not present on a mouse.</p>\n<p>[...] In many Windows applications, a similar functionality can be invoked with the <kbd>⇧ Shift</kbd>+<kbd>F10</kbd> keyboard shortcut [...].</p>\n</blockquote>\n<p>I used the Menu key (or sometimes <kbd>Shift</kbd>+<kbd>F10</kbd>) all the time.</p>\n<p><a href=\"https://apple.stackexchange.com/q/32715/111704\" class=\"link link-external\">Mac doesn't have a Menu key.</a>\nSuch a shame.\nAn accessibility issue as well.</p>\n<h3 id=\"alt-tabbing\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#alt-tabbing\">Alt-tabbing</a></h3>\n<p>The default alt-tabbing experience on Mac is poor.\nNo window previews like on Windows,\nand <a href=\"https://apple.stackexchange.com/q/112350/111704\" class=\"link link-external\">the keyboard shortcut for alt-tabbing to a hidden or minimized window is crazily complex</a>.</p>\n<p>Switching between the windows of a single app with <kbd>Cmd</kbd>+<kbd>`</kbd>\nis cool though.</p>\n<p>I use a free app called <a href=\"https://alt-tab-macos.netlify.app/\" class=\"link link-external\">AltTab</a>\nwhich &quot;brings the power of Windows's 'alt-tab' window switcher to macOS.&quot;\nUnfortunately it's a bit buggy\n(because <a href=\"https://alt-tab-macos.netlify.app/contributing\" class=\"link link-external\">&quot;Mac development ecosystem is pretty terrible in general&quot;</a>),\nbut it's 90% great and much better than the default alt-tabbing experience on Mac.</p>\n<h3 id=\"middle-clicking-on-the-trackpad\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#middle-clicking-on-the-trackpad\">Middle-clicking on the trackpad</a></h3>\n<p>I'm used to middle-clicking by tapping the trackpad with three fingers.\nThat's not possible on MacBook's trackpad though.</p>\n<p>The free app called <a href=\"https://github.com/artginzburg/MiddleClick-Ventura\" class=\"link link-external\">MiddleClick</a>\nfixes that,\nbut it's a bit buggy.</p>\n<p>There are paid alternatives,\nbut I'm not willing to pay several euros for a feature that should be part of core Mac.\n(I already said I'm a cheapo.)</p>\n<p>It's baffling that I need a 3rd party app for this basic feature.</p>\n<h3 id=\"gaming\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#gaming\">Gaming</a></h3>\n<p>I rarely play video games,\nbut I miss Battlefield 4.\nI'd also love to try Age of Empires IV.</p>\n<p>Mac runs some games, but of course not those two I'm interested in.</p>\n<p><a href=\"https://hanki.dev/just-do-it/\" class=\"link link-external\">My bro recently built a Windows gaming PC</a>,\nwhich made me want to build one too.</p>\n<h2 id=\"things-that-are-bad-both-in-windows-and-mac\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#things-that-are-bad-both-in-windows-and-mac\">Things that are bad both in Windows and Mac</a></h2>\n<h3 id=\"focus-stealing\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#focus-stealing\">Focus stealing</a></h3>\n<p>I truly <em>hated</em> focus stealing on Windows.\nIt's so annoying when you are doing something\nand another app/window suddenly steals focus,\ninterrupting your typing/clicking/whatever.</p>\n<p>Focus stealing is a problem on Mac too,\nalbeit not as big a problem.\n<a href=\"https://krisp.ai/\" class=\"link link-external\">Krisp</a> is maybe the worst offender I have encountered so far.</p>\n<p>I had <a href=\"https://news.ycombinator.com/item?id=31752926\" class=\"link link-external\">reaperducer's Hacker News comment about focus stealing on Macs</a>\nsaved on my bookmarks.\nIt's a good comment;\nhere's a snippet:</p>\n<blockquote>\n<p>I think allowing any pop-up to demand focus is a serious security flaw. I've sometimes found myself typing a password into a browser, or a word processor, because they've decided that they are the most important thing in my life at that moment.</p>\n</blockquote>\n<p>So, focus stealing is not only an annoyance but a security risk as well.</p>\n<h2 id=\"verdict\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#verdict\">Verdict</a></h2>\n<p>I like Mac because it's fast and a Unix system and generally just feels good.</p>\n<p>Mac required some tweaking to make me feel at home,\nbut what system doesn't require tweaking?\nThe Windows experience isn't optimal out of the box either.</p>\n<p>After a year of using Mac and touching Windows only a few times,\nI don't see myself going back to Windows, except maybe for gaming.\nThough it didn't take a full year for me to feel this way;\nonly a few days or weeks.</p>\n","summary":"I have used Windows PCs since I was a kid.\nA year ago (Jan 2022) I got my first MacBook.\nWhile Mac isn't perfect,\nit's so much better that I'm not going back to Windows.","date_published":"2023-01-31T12:00:00+03:00","tags":["Mac"]},{"id":"https://mtsknn.fi/blog/js-retry-on-fail/","url":"https://mtsknn.fi/blog/js-retry-on-fail/","title":"Automatically retrying a failing function in JS/TS","content_html":"<p>A little helper function\nto retry a function automatically a limited number of times (or until success)\nand with a certain delay between retries.\nUseful e.g. when working with a flaky API.</p>\n<h2 id=\"starting-point\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#starting-point\">Starting point</a></h2>\n<p>After a bit of googling,\nI found <a href=\"https://stackoverflow.com/a/42429685/1079869\" class=\"link link-external\">Jaromanda X's answer on Stack Overflow</a>\nto be a good starting point:</p>\n<blockquote>\n<p>Using a couple of helper functions I've used a lot, this becomes very easy</p>\n<p>The &quot;helpers&quot;</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">Promise<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">wait</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">time</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> time <span class=\"token operator\">||</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nPromise<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">cont<span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">err</span> <span class=\"token operator\">=></span> cont <span class=\"token operator\">></span> <span class=\"token number\">0</span> <span class=\"token operator\">?</span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>cont <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">:</span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token string\">'failed'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n<p>The code:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">myMainFuntion</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">var</span> delay <span class=\"token operator\">=</span> <span class=\"token number\">100</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">var</span> tries <span class=\"token operator\">=</span> <span class=\"token number\">3</span><span class=\"token punctuation\">;</span>\n      Promise<span class=\"token punctuation\">.</span><span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>tries<span class=\"token punctuation\">,</span> tryAsync<span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n</blockquote>\n<p>Works well,\nbut let's do better since the code wasn't immediately clear for me.</p>\n<h2 id=\"refactoring-step-by-step\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#refactoring-step-by-step\">Refactoring step by step</a></h2>\n<p>Let's do an unsolicited code review\nand refactor the code along the way.\nYou can also <a href=\"#final-result\" class=\"link link-anchor\">jump straight to the final result</a>.</p>\n<p>The general rule of code reviewing seems to apply here:\nthe shorter the code, the more it receives comments.</p>\n<h3 id=\"step-1-prettier\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-1-prettier\">Step 1: Prettier</a></h3>\n<p>The lines declaring the two helpers are very long: 80 and 161 characters.</p>\n<p>Too long lines are difficult to read.\nEspecially the <code>retry</code> method in this case.</p>\n<p>Let's format the code with Prettier:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">Promise<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">wait</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">time</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> time <span class=\"token operator\">||</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\nPromise<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">cont<span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">err</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n    cont <span class=\"token operator\">></span> <span class=\"token number\">0</span>\n      <span class=\"token operator\">?</span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>cont <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">:</span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token string\">'failed'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Now the structure of the code is much easier to see.\nLooks like <code>Promise.retry</code> is a recursive function.</p>\n<h3 id=\"step-2-no-monkey-patching\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-2-no-monkey-patching\">Step 2: no monkey patching</a></h3>\n<p><a href=\"https://stackoverflow.com/q/6223449/1079869\" class=\"link link-external\">Modifying global prototypes is generally not a good idea</a>,\nso instead of adding the methods to the global <code>Promise</code> object,\nlet's convert the methods to standalone functions.</p>\n<p>Diff:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>Promise<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">wait</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">time</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">const</span>   <span class=\"token function-variable function\">wait</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">time</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>  <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> time <span class=\"token operator\">||</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span>\n<span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>Promise<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">retry</span>      <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">cont<span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">cont<span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>  <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">err</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token prefix unchanged\"> </span>    cont <span class=\"token operator\">></span> <span class=\"token number\">0</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>      <span class=\"token operator\">?</span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>cont <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>      <span class=\"token operator\">?</span>         <span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>         <span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>cont <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>      <span class=\"token operator\">:</span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token string\">'failed'</span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token punctuation\">)</span>\n</span></span></code></pre>\n<p>Result (formatted with Prettier; <code>wait</code> function moved to the end):</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">cont<span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">err</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n    cont <span class=\"token operator\">></span> <span class=\"token number\">0</span>\n      <span class=\"token operator\">?</span> <span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>cont <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">:</span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token string\">'failed'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">wait</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">time</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> time <span class=\"token operator\">||</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h3 id=\"step-3-whats-cont\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-3-whats-cont\">Step 3: what's &quot;cont&quot;?</a></h3>\n<p>What is the <code>cont</code> parameter for –\nwhat does &quot;cont&quot; mean?</p>\n<p>I guess it means something like &quot;continue/retry if this is greater than 0.&quot;\nIn other words,\nit's the number of remaining retries.</p>\n<p>Let's rename <code>cont</code> to <code>retries</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">cont<span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">retries<span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token comment\">// ...</span>\n</span></span></code></pre>\n<h3 id=\"step-4-order-of-parameters\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-4-order-of-parameters\">Step 4: order of parameters</a></h3>\n<p>Is the order of the parameters ideal?</p>\n<p>Now the function reads like\n&quot;<code>retry</code> with x <code>retries</code> the given <code>fn</code> with a <code>delay</code> of y milliseconds.&quot;\nSounds clumsy.</p>\n<p>If the first two parameters are swapped:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">retries<span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">fn<span class=\"token punctuation\">,</span> retries<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</span></span></code></pre>\n<p>...the function reads like\n&quot;<code>retry</code> the given <code>fn</code> with x <code>retries</code> and a <code>delay</code> of y milliseconds.&quot;</p>\n<p>I think that's better –\nthe <code>fn</code> parameter is the &quot;main&quot; parameter,\nand the other two parameters are configurable options,\nso it makes sense to pass the function as the first parameter.</p>\n<h3 id=\"step-5-options-parameter\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-5-options-parameter\">Step 5: options parameter</a></h3>\n<p>Let's continue with the parameters.</p>\n<p>When calling <code>retry</code>, the purpose of the two option parameters is not clear:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>myFn<span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// What 2? What 200?</span>\n</span></code></pre>\n<p>Condensing the options into a single object\nmakes the call site clearer:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">fn<span class=\"token punctuation\">,</span> retries<span class=\"token punctuation\">,</span> delay</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">fn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> retries<span class=\"token punctuation\">,</span> delay <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token comment\">// Usage:</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>myFn<span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// huh?</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>myFn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">retries</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">delay</span><span class=\"token operator\">:</span> <span class=\"token number\">200</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// oh</span>\n</span></span></code></pre>\n<h3 id=\"step-6-delay-parameter\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-6-delay-parameter\">Step 6: delay parameter</a></h3>\n<p>Is the <code>delay</code> option clear?\nWhat does it delay – only the retries or also the initial function call?</p>\n<p>Let's rename <code>delay</code> to <code>retryInterval</code>\nto make it clear that it's only about the retries,\nnot the initial function call.\nActually, let's use <code>retryIntervalMs</code> to also clarify that the unit is milliseconds:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">fn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> retries<span class=\"token punctuation\">,</span> delay <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">fn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> retries<span class=\"token punctuation\">,</span> retryIntervalMs <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token comment\">// Usage:</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>myFn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">retries</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">delay</span><span class=\"token operator\">:</span> <span class=\"token number\">200</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>myFn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">retries</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">retryIntervalMs</span><span class=\"token operator\">:</span> <span class=\"token number\">200</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></span></code></pre>\n<p>Another benefit: now the two options are sorted alphabetically. :D</p>\n<p>Similarly, let's rename the <code>wait</code> function's <code>time</code> parameter to <code>ms</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">wait</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">time</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> time <span class=\"token operator\">||</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">wait</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">ms</span><span class=\"token punctuation\">)</span>   <span class=\"token operator\">=></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> ms   <span class=\"token operator\">||</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></span></code></pre>\n<h3 id=\"step-7-function-name\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-7-function-name\">Step 7: function name</a></h3>\n<p>More bike-shedding:\ndoes the name <code>retry</code> tell clearly what the function does?</p>\n<p>Yes, the function retries another function –\nbut only after the initial try, which is a <em>try</em>, not a <em>re-try</em>.</p>\n<p>Something like <code>tryAndRetry</code> or <code>retryOnFail</code> or <code>tryMultipleTimes</code> could be more descriptive.\nThough these are quite wordy and still not great.</p>\n<p>Apparently this whole thing is called &quot;retry pattern&quot; or &quot;retry design pattern&quot;\n(more about this in the <a href=\"#further-resources\" class=\"link link-anchor\"><em>Further resources</em> section</a>),\nso let's actually keep the <code>retry</code> name.</p>\n<p>But thinking about this was useful:\nwe can deduce that because the first <code>fn</code> call is a try and not a retry,\nthe maximum number of <code>fn</code> calls should be <code>1 + retries</code>.\nAnd indeed it is.\n(If the maximum number was something else,\nthe option name <code>retries</code> could be confusing.)</p>\n<p>Here's what we have so far:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">fn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> retries<span class=\"token punctuation\">,</span> retryIntervalMs <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">err</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n    retries <span class=\"token operator\">></span> <span class=\"token number\">0</span>\n      <span class=\"token operator\">?</span> <span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>retryIntervalMs<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n          <span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>fn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">retries</span><span class=\"token operator\">:</span> retries <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> retryIntervalMs <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">:</span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token string\">'failed'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">wait</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">ms</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> ms <span class=\"token operator\">||</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h3 id=\"step-8-better-error\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-8-better-error\">Step 8: better error</a></h3>\n<p>If the last retry also fails,\n<code>retry</code> throws the string <code>'failed'</code>.\nRe-throwing the error thrown by <code>fn</code> is likely more useful.</p>\n<p>While at it, let's also rename <code>err</code> to <code>error</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">retry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">fn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> retries<span class=\"token punctuation\">,</span> retryIntervalMs <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>  <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">err</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>  <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">error</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>    retries <span class=\"token operator\">></span> <span class=\"token number\">0</span>\n<span class=\"token prefix unchanged\"> </span>      <span class=\"token operator\">?</span> <span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>retryIntervalMs<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token prefix unchanged\"> </span>          <span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>fn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">retries</span><span class=\"token operator\">:</span> retries <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> retryIntervalMs <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix unchanged\"> </span>        <span class=\"token punctuation\">)</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>      <span class=\"token operator\">:</span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token string\">'failed'</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>      <span class=\"token operator\">:</span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">reject</span><span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>  <span class=\"token punctuation\">)</span>\n</span></span></code></pre>\n<h3 id=\"step-9-typescript\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-9-typescript\">Step 9: TypeScript</a></h3>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> retry <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span><span class=\"token punctuation\">(</span>\n  <span class=\"token function-variable function\">fn</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">{</span> retries<span class=\"token punctuation\">,</span> retryIntervalMs <span class=\"token punctuation\">}</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> retries<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span> retryIntervalMs<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span> <span class=\"token operator\">=></span>\n  <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n    retries <span class=\"token operator\">></span> <span class=\"token number\">0</span>\n      <span class=\"token operator\">?</span> <span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>retryIntervalMs<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n          <span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>fn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> retries<span class=\"token operator\">:</span> retries <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> retryIntervalMs <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token punctuation\">.</span><span class=\"token function\">reject</span><span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">wait</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>ms <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token builtin\">Promise</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> ms<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>The generic parameter <code>T</code> is used more than once in the function signature,\nso this is a good use of generics;\nsee <a href=\"https://effectivetypescript.com/2020/08/12/generics-golden-rule/\" class=\"link link-external\">The Golden Rule of Generics</a>.</p>\n<h3 id=\"step-10-the-need-for-async-await\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-10-the-need-for-async-await\">Step 10: the need for <code>async</code>/<code>await</code></a></h3>\n<p>So far so good,\nbut there's a caveat:</p>\n<p>The provided function must be async (marked with <code>async</code> or returns a Promise),\nor the function won't be retried.</p>\n<h4>Async functions</h4>\n<p>Async functions work well:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> options <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">retries</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">retryIntervalMs</span><span class=\"token operator\">:</span> <span class=\"token number\">200</span> <span class=\"token punctuation\">}</span>\n\n<span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token number\">42</span><span class=\"token punctuation\">,</span> options<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> returns 42</span>\n\n<span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">resolve</span><span class=\"token punctuation\">(</span><span class=\"token number\">42</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> options<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> returns 42</span>\n\n<span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'hi'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fail'</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> options<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> logs 'hi' (initial try)</span>\n<span class=\"token comment\">//=> logs 'hi' (1st retry)</span>\n<span class=\"token comment\">//=> logs 'hi' (2nd retry)</span>\n<span class=\"token comment\">//=> throws [Error: fail]</span>\n\n<span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'hi'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fail'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> options<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> logs 'hi' (initial try)</span>\n<span class=\"token comment\">//=> logs 'hi' (1st retry)</span>\n<span class=\"token comment\">//=> logs 'hi' (2nd retry)</span>\n<span class=\"token comment\">//=> throws [Error: fail]</span>\n</span></code></pre>\n<h4>Non-async functions</h4>\n<p>Non-async functions don't work:</p>\n<ol>\n<li>\n<p>Non-async functions are not retried:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'hi'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fail'</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> options<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> logs 'hi' (initial try)</span>\n<span class=\"token comment\">//=> throws [Error: fail]</span>\n</span></code></pre>\n</li>\n<li>\n<p>If a non-async function succeeds, a TypeError will still be thrown:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'hi'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> <span class=\"token number\">42</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> options<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> logs 'hi' (initial try)</span>\n<span class=\"token comment\">//=> throws [TypeError: fn(...).catch is not a function]</span>\n</span></code></pre>\n</li>\n</ol>\n<h4><code>async</code>/<code>await</code> vs Promises</h4>\n<p>TypeScript won't allow passing a non-async function to <code>retry</code>,\nbut I don't like that <code>retry</code> breaks at runtime if it's given a non-async function.</p>\n<p>Let's eliminate the caveat by refactoring to <code>async</code>/<code>await</code>.</p>\n<p>An alternative would be to make <code>retry</code> always return a Promise.\nThe code wouldn't be as clear in my opinion,\nand there would be the extra caveat of having to avoid the pitfalls of the\n<a href=\"https://stackoverflow.com/q/23803743/1079869\" class=\"link link-external\">explicit Promise constructor anti-pattern</a>.</p>\n<h3 id=\"step-11-async-await\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-11-async-await\">Step 11: <code>async</code>/<code>await</code></a></h3>\n<p>Diff:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token deleted-sign deleted language-ts\"><span class=\"token prefix deleted\">-</span><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> retry <span class=\"token operator\">=</span> <span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span><span class=\"token punctuation\">(</span>\n<span class=\"token prefix deleted\">-</span>  <span class=\"token function-variable function\">fn</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span><span class=\"token punctuation\">,</span>\n</span><span class=\"token inserted-sign inserted language-ts\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> retry <span class=\"token operator\">=</span> <span class=\"token generic-function\"><span class=\"token function\">async</span> <span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>\n<span class=\"token prefix inserted\">+</span>  <span class=\"token function-variable function\">fn</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span> <span class=\"token operator\">|</span> <span class=\"token constant\">T</span><span class=\"token punctuation\">,</span>\n</span><span class=\"token unchanged language-ts\"><span class=\"token prefix unchanged\"> </span>  <span class=\"token punctuation\">{</span> retries<span class=\"token punctuation\">,</span> retryIntervalMs <span class=\"token punctuation\">}</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> retries<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span> retryIntervalMs<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token punctuation\">}</span>\n</span><span class=\"token deleted-sign deleted language-ts\"><span class=\"token prefix deleted\">-</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span> <span class=\"token operator\">=></span>\n<span class=\"token prefix deleted\">-</span>  <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">catch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token prefix deleted\">-</span>    retries <span class=\"token operator\">></span> <span class=\"token number\">0</span>\n<span class=\"token prefix deleted\">-</span>      <span class=\"token operator\">?</span> <span class=\"token function\">wait</span><span class=\"token punctuation\">(</span>retryIntervalMs<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token prefix deleted\">-</span>          <span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>fn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> retries<span class=\"token operator\">:</span> retries <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> retryIntervalMs <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix deleted\">-</span>        <span class=\"token punctuation\">)</span>\n<span class=\"token prefix deleted\">-</span>      <span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token punctuation\">.</span><span class=\"token function\">reject</span><span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span>\n<span class=\"token prefix deleted\">-</span>  <span class=\"token punctuation\">)</span>\n</span><span class=\"token inserted-sign inserted language-ts\"><span class=\"token prefix inserted\">+</span><span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix inserted\">+</span>  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix inserted\">+</span>    <span class=\"token keyword\">return</span> <span class=\"token keyword\">await</span> <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix inserted\">+</span>  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix inserted\">+</span>    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>retries <span class=\"token operator\">&lt;=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix inserted\">+</span>      <span class=\"token keyword\">throw</span> error\n<span class=\"token prefix inserted\">+</span>    <span class=\"token punctuation\">}</span>\n<span class=\"token prefix inserted\">+</span>    <span class=\"token keyword\">await</span> <span class=\"token function\">sleep</span><span class=\"token punctuation\">(</span>retryIntervalMs<span class=\"token punctuation\">)</span>\n<span class=\"token prefix inserted\">+</span>    <span class=\"token keyword\">return</span> <span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>fn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> retries<span class=\"token operator\">:</span> retries <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> retryIntervalMs <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix inserted\">+</span>  <span class=\"token punctuation\">}</span>\n<span class=\"token prefix inserted\">+</span><span class=\"token punctuation\">}</span>\n</span>\n<span class=\"token deleted-sign deleted language-ts\"><span class=\"token prefix deleted\">-</span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">wait</span>  <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>ms <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token builtin\">Promise</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> ms<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token inserted-sign inserted language-ts\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">sleep</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>ms <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token builtin\">Promise</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> ms<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></span></code></pre>\n<p>(Heh, only one non-empty line remained unchanged.)</p>\n<p>Details:</p>\n<ul>\n<li>I renamed <code>wait</code> to <code>sleep</code> because <code>await wait()</code> would look awkward.</li>\n<li>This version works also with non-async functions\nbecause <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#conversion_to_promise\" class=\"link link-external\">the <code>await</code> operator converts non-Promise values to resolved Promises</a>.</li>\n<li><code>return</code> vs <code>return await</code>:\n<a href=\"https://jakearchibald.com/2017/await-vs-return-vs-return-await/\" class=\"link link-external\">&quot;Outside of try/catch blocks, <code>return await</code> is redundant.&quot;</a></li>\n</ul>\n<h2 id=\"final-result\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#final-result\">Final result</a></h2>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">/**\n * Runs the function `fn`\n * and retries automatically if it fails.\n *\n * Tries max `1 + retries` times\n * with `retryIntervalMs` milliseconds between retries.\n *\n * From https://mtsknn.fi/blog/js-retry-on-fail/\n */</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> retry <span class=\"token operator\">=</span> <span class=\"token generic-function\"><span class=\"token function\">async</span> <span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>\n  <span class=\"token function-variable function\">fn</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span> <span class=\"token operator\">|</span> <span class=\"token constant\">T</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">{</span> retries<span class=\"token punctuation\">,</span> retryIntervalMs <span class=\"token punctuation\">}</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> retries<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span> retryIntervalMs<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span><span class=\"token constant\">T</span><span class=\"token operator\">></span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">await</span> <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>error<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>retries <span class=\"token operator\">&lt;=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">throw</span> error\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">sleep</span><span class=\"token punctuation\">(</span>retryIntervalMs<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">retry</span><span class=\"token punctuation\">(</span>fn<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> retries<span class=\"token operator\">:</span> retries <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> retryIntervalMs <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">sleep</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>ms <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token builtin\">Promise</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> ms<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Looks quite good to me!</p>\n<h3 id=\"usage\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#usage\">Usage</a></h3>\n<p>Happy cases:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> options <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">retries</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">retryIntervalMs</span><span class=\"token operator\">:</span> <span class=\"token number\">200</span> <span class=\"token punctuation\">}</span>\n\n<span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token number\">42</span><span class=\"token punctuation\">,</span> options<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> 42</span>\n\n<span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">resolve</span><span class=\"token punctuation\">(</span><span class=\"token number\">42</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> options<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> 42</span>\n\n<span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token number\">42</span><span class=\"token punctuation\">,</span> options<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> 42</span>\n</span></code></pre>\n<p>Error cases:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> options <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">retries</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">retryIntervalMs</span><span class=\"token operator\">:</span> <span class=\"token number\">200</span> <span class=\"token punctuation\">}</span>\n\n<span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'hi'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fail'</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> options<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> logs 'hi' (initial try)</span>\n<span class=\"token comment\">//=> logs 'hi' (1st retry)</span>\n<span class=\"token comment\">//=> logs 'hi' (2nd retry)</span>\n<span class=\"token comment\">//=> throws [Error: fail]</span>\n\n<span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'hi'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> Promise<span class=\"token punctuation\">.</span><span class=\"token function\">reject</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fail'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> options<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> same as above</span>\n\n<span class=\"token function\">retry</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'hi'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fail'</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> options<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> same as above</span>\n</span></code></pre>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<p>This blog post was almost ready when I found\n<a href=\"https://stackoverflow.com/q/38213668/1079869\" class=\"link link-external\"><em>Promise Retry Design Patterns</em> on Stack Overflow</a>.\nThese three answers are interesting:</p>\n<ul>\n<li><a href=\"https://stackoverflow.com/a/55270741/1079869\" class=\"link link-external\">Red Mercury's answer</a>\nuses the <a href=\"https://en.wikipedia.org/wiki/Exponential_backoff\" class=\"link link-external\">exponential backoff algorithm</a>.\nThis led me to find\n<a href=\"https://github.com/sindresorhus/p-retry\" class=\"link link-external\">p-retry, a library that also uses the exponential backoff algorithm</a>.</li>\n<li><a href=\"https://stackoverflow.com/a/62442746/1079869\" class=\"link link-external\">Bryan McGrane's answer</a>\nis almost identical to my final result.</li>\n<li><a href=\"https://stackoverflow.com/a/63750676/1079869\" class=\"link link-external\">Nacho Coloma's answer</a>\nis also similar, but uses a <code>for</code> loop instead of recursion.\nThough this version retries only if the given function returns <code>undefined</code>,\nnot if it throws.</li>\n</ul>\n","summary":"A little helper function\nto retry a function automatically a limited number of times (or until success)\nand with a certain delay between retries.\nUseful e.g. when working with a flaky API.","date_published":"2022-12-14T12:00:00+03:00","date_modified":"2023-03-20T12:00:00+03:00","tags":["JavaScript","TypeScript"]},{"id":"https://mtsknn.fi/blog/swr-show-stale-data-on-revalidation-error/","url":"https://mtsknn.fi/blog/swr-show-stale-data-on-revalidation-error/","title":"SWR (v1): show previous/stale data on revalidation errors","content_html":"<p>If I'm already displaying data that I have successfully fetched with SWR,\nand then another fetch call fails (when revalidating the data),\nI don't want to replace the data view with an error view.\nHere's how to avoid that.</p>\n<h2 id=\"problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problem\">Problem</a></h2>\n<p>Basic usage of <a href=\"https://swr.vercel.app/\" class=\"link link-external\">SWR</a>\n(v1; I haven't upgraded to v2 yet):</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">useItems</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token punctuation\">,</span> error <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">useSWR</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/api/items/'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">url</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n    <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> res<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">items</span><span class=\"token operator\">:</span> data <span class=\"token operator\">??</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">isLoadingItems</span><span class=\"token operator\">:</span> <span class=\"token operator\">!</span>data <span class=\"token operator\">&amp;&amp;</span> <span class=\"token operator\">!</span>error<span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">failedToLoadItems</span><span class=\"token operator\">:</span> <span class=\"token operator\">!</span><span class=\"token operator\">!</span>error<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">Items</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> items<span class=\"token punctuation\">,</span> isLoadingItems<span class=\"token punctuation\">,</span> failedToLoadItems <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">useItems</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> isLoadingItems <span class=\"token operator\">?</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Loading...</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span> <span class=\"token operator\">:</span> failedToLoadItems <span class=\"token operator\">?</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Error</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span> <span class=\"token operator\">:</span> items<span class=\"token punctuation\">.</span>length <span class=\"token operator\">===</span> <span class=\"token number\">0</span> <span class=\"token operator\">?</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">No items</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span> <span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>ul</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token punctuation\">{</span>items<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>li</span> <span class=\"token attr-name\">key</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>item<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>item<span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>li</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>ul</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>My problem occurs with these steps:</p>\n<ol>\n<li>SWR starts to fetch initial data, app shows loading state.</li>\n<li>SWR has fetched data, app shows data.</li>\n<li>SWR starts to fetch data again (revalidation), app still shows data.</li>\n<li>SWR fails to fetch data (e.g. the API endpoint fails), app shows error state.\n<ul>\n<li>Here I don't want the app to show error state;\nI want it to keep showing the previous (stale) data.</li>\n</ul>\n</li>\n</ol>\n<h2 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h2>\n<p>I spent some time looking for a solution\nuntil I realized that it's very simple –\nlet the failed state be <code>false</code>\nonly if there's no data (i.e. if <code>data</code> is not falsy):</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">useItems</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> data<span class=\"token punctuation\">,</span> error <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">useSWR</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/api/items/'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">url</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n    <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> res<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">items</span><span class=\"token operator\">:</span> data <span class=\"token operator\">??</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">isLoadingItems</span><span class=\"token operator\">:</span> <span class=\"token operator\">!</span>data <span class=\"token operator\">&amp;&amp;</span> <span class=\"token operator\">!</span>error<span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">failedToLoadItems</span><span class=\"token operator\">:</span> <span class=\"token operator\">!</span>data <span class=\"token operator\">&amp;&amp;</span> <span class=\"token operator\">!</span><span class=\"token operator\">!</span>error<span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">//                 ^^^^^^^^</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">Items</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// Same as before</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h2 id=\"truth-tables\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#truth-tables\">Truth tables</a></h2>\n<p>I built these truth tables to be sure that the change works as expected.</p>\n<h3 id=\"before-the-change\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#before-the-change\">Before the change</a></h3>\n<table>\n<thead>\n<tr>\n<th><code>data</code></th>\n<th><code>error</code></th>\n<th><code>isLoading</code><br>(<code>!data &amp;&amp; !error</code>)</th>\n<th><code>failedToLoad</code><br>(<code>!!error</code>)</th>\n<th>state description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>falsy</td>\n<td>falsy</td>\n<td><code>true</code></td>\n<td><code>false</code></td>\n<td>initial state or revalidation succeeded</td>\n</tr>\n<tr>\n<td>truthy</td>\n<td>falsy</td>\n<td><code>false</code></td>\n<td><code>false</code></td>\n<td>initial fetch succeeded</td>\n</tr>\n<tr>\n<td>falsy</td>\n<td>truthy</td>\n<td><code>false</code></td>\n<td><code>true</code></td>\n<td>initial fetch failed</td>\n</tr>\n<tr>\n<td>truthy</td>\n<td>truthy</td>\n<td><code>false</code></td>\n<td><code>true</code></td>\n<td>revalidation failed</td>\n</tr>\n</tbody>\n</table>\n<h3 id=\"after-the-change\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#after-the-change\">After the change</a></h3>\n<p>Only the 4th column and the last row are different:</p>\n<table>\n<thead>\n<tr>\n<th><code>data</code></th>\n<th><code>error</code></th>\n<th><code>isLoading</code><br>(<code>!data &amp;&amp; !error</code>)</th>\n<th><code>failedToLoad</code><br>(<code>!data &amp;&amp; !!error</code>)</th>\n<th>state description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>falsy</td>\n<td>falsy</td>\n<td><code>true</code></td>\n<td><code>false</code></td>\n<td>initial state or revalidation succeeded</td>\n</tr>\n<tr>\n<td>truthy</td>\n<td>falsy</td>\n<td><code>false</code></td>\n<td><code>false</code></td>\n<td>initial fetch succeeded</td>\n</tr>\n<tr>\n<td>falsy</td>\n<td>truthy</td>\n<td><code>false</code></td>\n<td><code>true</code></td>\n<td>initial fetch failed</td>\n</tr>\n<tr>\n<td>truthy</td>\n<td>truthy</td>\n<td><code>false</code></td>\n<td><code>false</code> (<code>true</code> in the other table)</td>\n<td>revalidation failed</td>\n</tr>\n</tbody>\n</table>\n<p>LGTM!</p>\n","summary":"If I'm already displaying data that I have successfully fetched with SWR,\nand then another fetch call fails (when revalidating the data),\nI don't want to replace the data view with an error view.\nHere's how to avoid that.","date_published":"2022-11-08T12:00:00+03:00","date_modified":"2023-05-12T12:00:00+03:00","tags":["JavaScript","React"]},{"id":"https://mtsknn.fi/blog/burnout/","url":"https://mtsknn.fi/blog/burnout/","title":"Wild ideas to avoid burnout","content_html":"<p>Care less,\ntake a sabbatical,\nget a second job,\nget monster ear protection,\nhide behind your manager.</p>\n<p>Someone recently <a href=\"https://news.ycombinator.com/item?id=33260525\" class=\"link link-external\">asked on Hacker News if anyone has managed to find enjoyment in their work after burnout</a>.</p>\n<p>Excerpts from comments that I liked\n(not all are that wild, but anyway):</p>\n<ul>\n<li>\n<p><a href=\"https://news.ycombinator.com/item?id=33261345\" class=\"link link-external\"><strong>Care less</strong></a>\n(comment by highwaylights)</p>\n<blockquote>\n<p>This advice is going to be hated by a lot of people but... care less.</p>\n<p>You're in an industry that values your skills and seems to always have demand.\nWhether or not that's true in the future,\nit certainly is <em>today</em>,\nso the best thing you can do is put yourself first and worry less about work as a whole.\nEspecially if it's not your own company.</p>\n<p>Spend more time on you, on activities with friends and family, on hobbies.\nIf you don't have particularly healthy hobbies, maybe start some.\nGetting <em>away</em> from your work more and more will make it all the more bearable.</p>\n<p>These days work is close to the bottom of my list of concerns,\nwhich sounds really bad – BUT –\nI find I'm more productive than I've been in years\n<em>because</em> I'm not worrying if something takes longer than expected or is bigger than I realised.\nI can just enjoy the problem solving and shipping without the stress.</p>\n<p>If you're burned out this badly I'd suggest it's your soul's way of telling yourself\n&quot;hey this isn't working for me&quot;.\nAs someone who has been to some pretty messed up depths with the anxiety monster,\nI'd heed that voice.\nLife isn't long enough to stay stuck in a rut like this for any amount of time at all.</p>\n</blockquote>\n<p>Caring less doesn't mean doing sloppy work,\nnor does it mean not caring about the quality of your work.</p>\n<p>It means not taking work things too seriously (no one's life is at stake),\nnot having too strong opinions on everything (so you can avoid tiring debates; pick your battles),\nnot involving in office politics.</p>\n<p>It means focusing on the fun things,\nlike technical challenges,\nclean code practices (yea I like them),\ndocumenting your learnings (and later blogging about them).</p>\n<p>Another way of saying &quot;care less&quot; (comment by _fat_santa):</p>\n<blockquote>\n<p>I think a better way of putting it is: &quot;don't get too emotionally invested&quot;.\nWriting code I often times get emotionally invested in its success,\nbut with a job you have to create that clear line of &quot;I care, but I'm not emotionally invested&quot;.\nI really want to fix that bug in the system but if 5PM hits,\nI don't have the emotional investment to keep going into the night like with a side project,\nI can get to it in the morning.</p>\n</blockquote>\n<p>Stopping early and continuing the next day is important,\nbut sometimes I fail to do that.\nLike &quot;one more turn&quot; when playing Civilization,\n&quot;ten more minutes&quot; often gets out of hand (comment by SoftTalker):</p>\n<blockquote>\n<p>I used to be the sort of person who would stay at work\nuntil I got to what felt like a &quot;good&quot; stopping point in what I was doing.\nIf something wasn't working,\nand seemed like another 30 minutes might fix it,\nI would often do it.\nOf course 30 minutes often turned into 3 hours.</p>\n<p>Now I'm much more likely to just go home at the end of the day.\nI don't really care what fires are burning,\nthey will be there tomorrow and nobody will die.</p>\n</blockquote>\n<p>Saying &quot;care less&quot; is like saying &quot;stress less,&quot;\nbut the latter is difficult to implement on its own;\nyou need other tools for that (e.g. caring less).</p>\n</li>\n<li>\n<p><a href=\"https://news.ycombinator.com/item?id=33261218\" class=\"link link-external\"><strong>Take a sabbatical</strong></a>\n(comments by samatman)</p>\n<blockquote>\n<p>Can you afford a sabbatical?</p>\n<p>The term has roots in &quot;seven&quot;, just like Sabbath,\nso with 14 years in the industry,\nyou've skipped one.</p>\n<p>I think a solid half-year off every seven to ten years\nis an excellent practice for intellectually demanding labor.</p>\n</blockquote>\n<p>Ooh, I suddenly feel like I'd like to take a sabbatical!\nEven 6 months would be nice.</p>\n<p>A sabbatical is not a vacation;\nit's time to work on your passion projects:</p>\n<blockquote>\n<p>What makes it a sabbatical is that it's a change of pace which furthers your career.\nI'd love to take a sabbatical and just work on building stuff, personally:\nfrom soldering, to designing boards, probing them, writing firmware,\nI'm a rank amateur, and I'd love to level up for a while,\nwhile free of the burden of justifying my salary in the process.</p>\n<p>[...]</p>\n<p>What it isn't, is a vacation, or a convalescence<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup>.\nIt sounds like you haven't hit &quot;can't work&quot;, just &quot;sucks to work&quot;, which is a blessing.</p>\n<p>[...]</p>\n<p>I've known people who pushed &quot;sucks to work&quot; until it became &quot;can't work&quot;,\nand they don't all make it back to the profession,\na bit less than half in fact.\nWhatever you do,\nI urge you to take burnout seriously,\nbecause at the limit it's a serious medical condition which can cripple or kill you.</p>\n</blockquote>\n<p>When looking for a job after the sabbatical,\nsaying that you took a sabbatical sounds better\nthan saying that you took a year off:</p>\n<blockquote>\n<p>A sabbatical is not a gap in one's resume, it's a sabbatical.\nWhat's done with that time is open-ended,\nbut one of the things it does is provide a useful line item.</p>\n<p>[...]</p>\n<p>A sabbatical is good if:\nyou can write an open-source library in your niches which solves a problem,\nif you expand the breadth of your expertise,\nif what you're doing has a significant philanthropic angle.\nAll of these can set someone up for a higher tier of job,\nsomething with better prospects.</p>\n</blockquote>\n<p>Even if you wouldn't achieve anything tangible during your sabbatical,\nit should still bring great benefits:\nless stress, fresh mind, more energy.</p>\n<p>If you are already burned out,\ntaking a year off might be better for your health.\nIf you are not,\na sabbatical could be a good tool to avoid that.</p>\n</li>\n<li>\n<p><a href=\"https://news.ycombinator.com/item?id=33261325\" class=\"link link-external\"><strong>Get a second job</strong></a>\n(comment by aspyct)</p>\n<blockquote>\n<p>For me, switching to 4 days a week was a huge improvement in my quality of life.\nNow I've switched to 3,\nand have a completely different second job 2 days a week.\nAnd two days of rest.</p>\n<p>I love both, for different reasons,\nbut could never go back to a full week of IT,\nor switch to a full week of my new job.\nThey balance fine,\nbut I don't think they can exist on their own.</p>\n</blockquote>\n<p>Interesting idea!</p>\n<p>Having only one job and working only 3 or 4 days a week would be more optimal,\nbut not always financially feasible.\nGetting a completely different second job would bring in more money\nand also variation to days.</p>\n<p>Another good point:</p>\n<blockquote>\n<p>I think working 5 days a week, 8h a day on the same topic is insane.\nI can't think of anything I would want to do for so many hours a day, almost every day.</p>\n</blockquote>\n<p>It's crazy how much people usually work (including me, ouch).</p>\n</li>\n<li>\n<p><a href=\"https://news.ycombinator.com/item?id=33260975\" class=\"link link-external\"><strong>Get monster ear protection</strong></a>\n(comment by larve)</p>\n<blockquote>\n<p>Get monster ear protection.\nThis has to be one of the revelations:\nnoise stresses me out <em>sooo</em> much,\nand I have been repressing it for so long.\nI now have -35 dB + noise-cancelling earplugs\nand can basically live in complete silence, in the noisiest environment.</p>\n</blockquote>\n<p>Noise stresses me out too, very much so,\nand I too have been ignoring that.</p>\n<p>I walk on my treadmill for several hours every work day<sup><a aria-label=\"footnote 2\" class=\"link\" href=\"#fn-2\" id=\"fnref-2\">[2]</a></sup>,\nand it's not a particularly silent device,\nso I'm being exposed to a lot of noise.\nThat's gotta do something to my mind,\nand not in a good way.</p>\n<p>I already own <a href=\"https://www.sony.com/ug/electronics/headband-headphones/wh-1000xm3\" class=\"link link-external\">Sony WH-1000XM3 wireless noise-cancelling headphones</a>,\nand looks like <a href=\"https://www.rtings.com/headphones/tools/table/94793\" class=\"link link-external\">RTINGS.com has rated them as having the best level of noise isolation</a>\namong 697 headphones that they have tested (as of Nov 7, 2022).\nNice!\nSo I just need to learn to use my headphones more.</p>\n</li>\n<li>\n<p><a href=\"https://news.ycombinator.com/item?id=33260963\" class=\"link link-external\"><strong>Hide behind your manager</strong></a>\n(comment by jnsaff2)<sup><a aria-label=\"footnote 3\" class=\"link\" href=\"#fn-3\" id=\"fnref-3\">[3]</a></sup></p>\n<blockquote>\n<p>Whenever anyone asked me to stay late\nI referred them to my manager for them to approve paid overtime.\nMost of the time the discussion stopped there,\nbut there were a few times when they decided to pay.</p>\n</blockquote>\n<p>There'll always be people who'll try to shamelessly take advantage of you.</p>\n<p>If setting boundaries on your own feels difficult,\none strategy is to delegate unpleasant discussions to another person,\nlike your manager.</p>\n<p>That's not cowardice;\nthat's saving your mental energy\nby avoiding discussions that shouldn't happen in the first place\n(no one should ask or except anyone to do unpaid overtime).</p>\n</li>\n</ul>\n<p>One more link:\n<a href=\"https://www.youtube.com/watch?v=HU0pZarehLY\" class=\"link link-external\"><em>Do Nothing</em> on YouTube (0:49) summarizes the &quot;care less&quot; category well</a>.</p>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p><a href=\"https://www.dictionary.com/browse/convalescence\" class=\"link link-external\">Dictionary.com defines <em>convalescence</em></a>\nas &quot;the gradual recovery of health and strength after illness.&quot; <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n<li id=\"fn-2\"><p>Walking is <em>sooo</em> much better than sitting or standing still.\nBlog post coming some day. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-2\">↩</a></p>\n</li>\n<li id=\"fn-3\"><p>To be clear,\nthe comment doesn't directly say to &quot;hide behind your manager.&quot; <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-3\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"Care less,\ntake a sabbatical,\nget a second job,\nget monster ear protection,\nhide behind your manager.","date_published":"2022-11-07T12:00:00+03:00","tags":["Work–life balance"]},{"id":"https://mtsknn.fi/blog/react-fallback-ref/","url":"https://mtsknn.fi/blog/react-fallback-ref/","title":"Four ways to use a fallback ref with `React.forwardRef`","content_html":"<p>A component wrapped in <code>React.forwardRef</code>\nnormally expects to receive a ref from its parent component.\nWhat if you want the component to have a ref\neven if it doesn't receive one from its parent?</p>\n<h2 id=\"the-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-problem\">The problem</a></h2>\n<p><a href=\"https://reactjs.org/docs/react-api.html#reactforwardref\" class=\"link link-external\"><code>React.forwardRef</code></a> allows passing a ref to a child component:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">Parent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Child</span></span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> Child <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>forwardedRef<span class=\"token operator\">?.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>forwardedRef<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>forwardedRef<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">...</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>When rendering <code>&lt;Parent /&gt;</code>,\nboth <code>useEffect</code>s log the <code>&lt;div&gt;</code> element from the <code>Child</code> component:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Parent</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token comment\">// Logs `&lt;div>` twice</span>\n</span></code></pre>\n<p>But when rendering <code>&lt;Child /&gt;</code> on its own,\nthe <code>Child</code>'s <code>useEffect</code> logs <code>undefined</code>\nbecause no ref is passed/forwarded:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Child</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token comment\">// Logs `undefined` once 😿</span>\n</span></code></pre>\n<p>More specifically:\n<code>forwardedRef</code> is <code>null</code>,\nso <code>forwardedRef?.current</code> is <code>undefined</code>.</p>\n<p>What if you want <code>Child</code> to have a ref to its <code>&lt;div&gt;</code> element\neven when no ref is forwarded to <code>Child</code>?\nYou can create a new ref in <code>Child</code>,\nbut you can't attach both refs to the <code>&lt;div&gt;</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> Child <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span>\n      <span class=\"token comment\">// 🛑 JSX elements cannot have multiple attributes with the same name</span>\n      <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>forwardedRef<span class=\"token punctuation\">}</span></span>\n      <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span>\n    <span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      ...\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h2 id=\"desired-outcome\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#desired-outcome\">Desired outcome</a></h2>\n<p>When rendering <code>&lt;Parent /&gt;</code>,\nboth <code>useEffect</code>s should still log the <code>&lt;div&gt;</code> element from the <code>Child</code> component:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Parent</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token comment\">// Logs `&lt;div>` twice</span>\n</span></code></pre>\n<p>When rendering <code>&lt;Child&gt;</code> on its own,\nits <code>useEffect</code> should log the <code>&lt;div&gt;</code> element:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Child</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token comment\">// Logs `&lt;div>` once</span>\n</span></code></pre>\n<h2 id=\"four-solutions\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#four-solutions\">Four solutions</a></h2>\n<h3 id=\"useref-oror\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#useref-oror\"><code>useRef</code> + <code>||</code></a></h3>\n<p>Create a fallback ref with <code>useRef</code>\nand use it if <code>forwardedRef</code> is <code>null</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> Child <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> fallbackRef <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> forwardedRef <span class=\"token operator\">||</span> fallbackRef\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>ref<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">...</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Or as a custom hook:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> Child <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token function\">useFallbackRef</span><span class=\"token punctuation\">(</span>forwardedRef<span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>ref<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">...</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">useFallbackRef</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> fallbackRef <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> forwardedRef <span class=\"token operator\">||</span> fallbackRef\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Pros:</p>\n<ul>\n<li>Very simple and clear.\nMaybe the simplest solution.</li>\n</ul>\n<p>Cons:</p>\n<ul>\n<li>The fallback ref is created even when it's not needed,\nbut it's probably not a big deal.</li>\n</ul>\n<p>I found this solution via\n<a href=\"https://github.com/brunoscopelliti/use-forward-ref\" class=\"link link-external\"><code>brunoscopelliti/use-forward-ref</code> on GitHub</a>,\nwhich was the only npm package found via\n<a href=\"https://www.npmjs.com/search?q=react%20fallback%20ref\" class=\"link link-external\">searching &quot;react fallback ref&quot; on npmjs.com</a>.</p>\n<h3 id=\"lazy-usestate-conditional-createref\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#lazy-usestate-conditional-createref\">Lazy <code>useState</code> + conditional <code>createRef</code></a></h3>\n<p>Create a fallback ref with <a href=\"https://reactjs.org/docs/react-api.html#reactcreateref\" class=\"link link-external\"><code>React.createRef</code></a>\nif <code>forwardedRef</code> is <code>null</code>.</p>\n<p>To avoid creating a new ref every time the <code>Child</code> component is re-rendered,\nwrap the conditional ref creation in <code>useState</code>\nand use <a href=\"https://reactjs.org/docs/hooks-reference.html#lazy-initial-state\" class=\"link link-external\">lazy initial state</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> Child <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>ref<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> forwardedRef <span class=\"token operator\">||</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">createRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>ref<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">...</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Or as a custom hook:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> Child <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token function\">useFallbackRef</span><span class=\"token punctuation\">(</span>forwardedRef<span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>ref<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">...</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">useFallbackRef</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> forwardedRef <span class=\"token operator\">||</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">createRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Pros:</p>\n<ul>\n<li>Simple and reads like English:\nuse <code>forwardedRef</code> if it exists (i.e. if it's not <code>null</code>),\notherwise create a new ref.</li>\n<li>The fallback ref is created only when necessary.\n(Probably doesn't matter though.)</li>\n</ul>\n<p>Cons:</p>\n<ul>\n<li>It's mildly confusing to mix hooks and the non-hook <code>createRef</code> function.</li>\n<li>Simple because uses only the <code>useState</code> hook,\nthough you do need to know about and understand <a href=\"https://reactjs.org/docs/hooks-reference.html#lazy-initial-state\" class=\"link link-external\">lazy initial state</a>.</li>\n</ul>\n<p>I came up with this solution myself. 🤙</p>\n<h3 id=\"useimperativehandle\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#useimperativehandle\"><code>useImperativeHandle</code></a></h3>\n<p><a href=\"https://reactjs.org/docs/hooks-reference.html#useimperativehandle\" class=\"link link-external\">React's <code>useImperativeHandle</code> hook</a>\nallows customizing the value of a forwarded ref.</p>\n<p>The docs for the hook are very terse\n(emphasis added and example code slightly modified):</p>\n<blockquote>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">useImperativeHandle</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">,</span> createHandle<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>deps<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p><strong><code>useImperativeHandle</code> customizes the instance value\nthat is exposed to parent components when using <code>ref</code>.</strong>\nAs always, imperative code using refs should be avoided in most cases.\n<strong><code>useImperativeHandle</code> should be used with <a href=\"https://reactjs.org/docs/react-api.html#reactforwardref\" class=\"link link-external\"><code>forwardRef</code></a>:</strong></p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> FancyInput <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useImperativeHandle</span><span class=\"token punctuation\">(</span>forwardedRef<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token function-variable function\">focus</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">.</span><span class=\"token function\">focus</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>In this example,\na parent component that renders <code>&lt;FancyInput ref={inputRef} /&gt;</code>\nwould be able to call <code>inputRef.current.focus()</code>.</p>\n</blockquote>\n<p>In the quoted example,\n<code>inputRef.current</code> has <em>only</em> the <code>focus</code> method.</p>\n<p>To expose all methods and properties of the DOM element to the parent ref,\nexpose <code>ref.current</code> as-is:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">useImperativeHandle</span><span class=\"token punctuation\">(</span>forwardedRef<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Then:</p>\n<ul>\n<li>If a ref is forwarded,\n<code>forwardedRef.current</code> and <code>ref.current</code> will point to the same DOM element.</li>\n<li>If a ref is not forwarded,\n<code>forwardedRef</code> will still be <code>null</code>,\nbut <code>ref</code> will act as a fallback ref.</li>\n</ul>\n<p>Put together:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> Child <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n  <span class=\"token function\">useImperativeHandle</span><span class=\"token punctuation\">(</span>forwardedRef<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// `ref` is not needed here</span>\n\n  <span class=\"token comment\">// Notice we are using `ref` here, not `forwardedRef`</span>\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">...</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Or as a custom hook:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> Child <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token function\">useFallbackRef</span><span class=\"token punctuation\">(</span>forwardedRef<span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>ref<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// `ref` is needed again here</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">...</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">useFallbackRef</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n  <span class=\"token function\">useImperativeHandle</span><span class=\"token punctuation\">(</span>forwardedRef<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> ref\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Pros:</p>\n<ul>\n<li>The <code>useImperativeHandle</code> hook is designed for\ncustomizing the value of the forwarded ref,\nso using it like this feels correct.</li>\n<li>ESLint knows that the <code>ref</code> variable is stable,\nso it doesn't need to be included in the <code>useEffect</code>'s dependency array,\nmaking the code clearer.\n(Though ESLint won't know that <code>ref</code> is stable when using the custom hook.)</li>\n</ul>\n<p>Cons:</p>\n<ul>\n<li>The <code>useImperativeHandle</code> hook is exotic and probably only rarely used.\nIf you have never heard of it before,\nyou can't know how it works at a first glance.\n(Compared to <code>useState</code>,\nwhich is maybe the most basic hook\nand should be familiar to any React dev.)</li>\n<li>The fallback ref is created even when it's not needed,\nbut it's probably not a big deal.</li>\n</ul>\n<p>I found this solution via\n<a href=\"https://jaketrent.com/post/fallback-ref-react\" class=\"link link-external\">Jake Trent's blog post <em>Fallback Ref in React</em></a>.\nIt introduced me to the <code>useImperativeHandle</code> hook\nand has good musings about why the hook is <em>lame</em>.</p>\n<h3 id=\"merge-refs\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#merge-refs\">Merge refs</a></h3>\n<p>Use a <a href=\"https://github.com/gregberge/react-merge-refs\" class=\"link link-external\">utility like <code>react-merge-refs</code></a> to merge the two refs:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> mergeRefs <span class=\"token keyword\">from</span> <span class=\"token string\">'react-merge-refs'</span>\n\n<span class=\"token keyword\">const</span> Child <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token function\">mergeRefs</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>forwardedRef<span class=\"token punctuation\">,</span> ref<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">...</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Under the hood <code>mergeRefs</code> returns a <a href=\"https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node\" class=\"link link-external\">callback ref</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">mergeRefs</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">refs</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    refs<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">ref</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">typeof</span> ref <span class=\"token operator\">===</span> <span class=\"token string\">'function'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">ref</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>ref <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        ref<span class=\"token punctuation\">.</span>current <span class=\"token operator\">=</span> value\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>If <code>forwardedRef</code> is <code>null</code>,\n<code>mergeRefs</code> simply skips it.</p>\n<p>Pros:</p>\n<ul>\n<li>More versatile because supports merging more than two refs.</li>\n</ul>\n<p>Cons:</p>\n<ul>\n<li>Requires a 3rd party library\nunless you re-implement the utility yourself.\nAnd if you are going to re-implement it yourself,\nthere are simpler solutions (unless you need to merge more than two refs).</li>\n</ul>\n<p>The <a href=\"https://github.com/theKashey/use-callback-ref\" class=\"link link-external\"><code>use-callback-ref</code> library</a>\nprovides a similar <code>mergeRefs</code> function\nas well as a <code>useMergeRefs</code> hook.</p>\n<h2 id=\"which-solution-to-use\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#which-solution-to-use\">Which solution to use?</a></h2>\n<p>If you need to merge more than two refs,\nuse a utility like <a href=\"https://github.com/gregberge/react-merge-refs\" class=\"link link-external\"><code>react-merge-refs</code></a> or <a href=\"https://github.com/theKashey/use-callback-ref\" class=\"link link-external\"><code>use-callback-ref</code></a>.</p>\n<p>Otherwise I would prefer the first or the second solution.\nBoth are simple and straightforward.\nNo point in installing a dependency if you just want to create a fallback ref.</p>\n<p>Initially the <code>useImperativeHandle</code> solution felt the most correct to me\nbecause the hook is designed for customizing the value of the forwarded ref.\nHowever, the solution is more difficult to understand\nbecause the hook is so exotic and rarely used.</p>\n<p>One more point to consider:\nwhat if,\nduring the lifecycle of <code>Child</code> (the component wrapped in <code>React.forwardRef</code>),\n<code>forwardedRef</code> is sometimes <code>null</code> and sometimes a ref object?\nI haven't tested how the different solutions would react to that.\nThis might or might not be relevant in your case.</p>\n<h2 id=\"invalid-solutions\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#invalid-solutions\">Invalid solutions</a></h2>\n<p>Knowing what <em>doesn't</em> work can be very useful as well.</p>\n<h3 id=\"conditional-useref\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#conditional-useref\">Conditional <code>useRef</code></a></h3>\n<p>Creating a fallback ref with <code>useRef</code> if <code>forwardedRef</code> is <code>null</code> might work:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> Child <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 🛑 Not allowed!</span>\n  <span class=\"token comment\">// Calling hooks conditionally breaks the rules of hooks.</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> forwardedRef <span class=\"token operator\">||</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>ref<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">...</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>...but it might also not work\nbecause calling hooks conditionally breaks the <a href=\"https://reactjs.org/docs/hooks-rules.html\" class=\"link link-external\">rules of hooks</a>.</p>\n<h3 id=\"conditional-createref\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#conditional-createref\">Conditional <code>createRef</code></a></h3>\n<p><a href=\"https://reactjs.org/docs/react-api.html#reactcreateref\" class=\"link link-external\"><code>React.createRef</code></a> is not a hook,\nso it can be called conditionally:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> Child <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 🛑 A new ref is created on every re-render if `forwardedRef` is `null`</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> forwardedRef <span class=\"token operator\">||</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">createRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>ref<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">...</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>However,\nif <code>forwardedRef</code> is <code>null</code>,\na new ref is created every time the <code>Child</code> component is re-rendered.</p>\n<p>Furthermore,\n<code>ref</code> is not stable,\nso it needs to be included in the <code>useEffect</code>'s dependency array.\nBecause of this, the <code>useEffect</code> will run on every re-render.</p>\n<p>Omitting <code>ref</code> from the dependency array\nand ignoring the &quot;exhaustive-deps&quot; ESLint rule\nis not a proper solution.\nIgnoring the rule is confusing and opens the door for further problems.</p>\n<h3 id=\"useref-conditional-createref\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#useref-conditional-createref\"><code>useRef</code> + conditional <code>createRef</code></a></h3>\n<p>To make the <code>ref</code> from the previous invalid solution stable,\nwe can wrap it in another <code>useRef</code>\nand immediately grab its current value (i.e. <code>forwardedRef</code> or the newly-created ref):</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> Child <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">forwardRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> forwardedRef</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 🛑 A new ref is created and discarded on every re-render if `forwardedRef` is `null`</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token function\">useRef</span><span class=\"token punctuation\">(</span>forwardedRef <span class=\"token operator\">||</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">createRef</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>current\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>ref<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// `ref` must be included here but is stable</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">...</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>However,\nif <code>forwardedRef</code> is <code>null</code>,\na new ref is still created every time the <code>Child</code> component is re-rendered;\nit's just discarded immediately.</p>\n<p>Plus the code looks ugly and unclear.</p>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<p>This blog post contains these links:</p>\n<ul>\n<li><a href=\"https://jaketrent.com/post/fallback-ref-react\" class=\"link link-external\">Jake Trent's blog post <em>Fallback Ref in React</em></a></li>\n<li><a href=\"https://www.npmjs.com/search?q=react%20fallback%20ref\" class=\"link link-external\">Searching &quot;react fallback ref&quot; on npmjs.com</a></li>\n<li>Projects on GitHub:\n<ul>\n<li><a href=\"https://github.com/gregberge/react-merge-refs\" class=\"link link-external\"><code>react-merge-refs</code></a></li>\n<li><a href=\"https://github.com/theKashey/use-callback-ref\" class=\"link link-external\"><code>use-callback-ref</code></a></li>\n<li><a href=\"https://github.com/brunoscopelliti/use-forward-ref\" class=\"link link-external\"><code>use-forward-ref</code></a></li>\n</ul>\n</li>\n<li>React docs:\n<ul>\n<li><a href=\"https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node\" class=\"link link-external\">Callback refs</a></li>\n<li><a href=\"https://reactjs.org/docs/forwarding-refs.html\" class=\"link link-external\"><em>Forwarding Refs</em></a></li>\n<li><a href=\"https://reactjs.org/docs/hooks-reference.html#lazy-initial-state\" class=\"link link-external\"><em>Lazy initial state</em> (<code>useState</code>)</a></li>\n<li><a href=\"https://reactjs.org/docs/react-api.html#reactcreateref\" class=\"link link-external\"><code>React.createRef</code></a></li>\n<li><a href=\"https://reactjs.org/docs/react-api.html#reactforwardref\" class=\"link link-external\"><code>React.forwardRef</code></a></li>\n<li><a href=\"https://reactjs.org/docs/hooks-rules.html\" class=\"link link-external\"><em>Rules of Hooks</em></a></li>\n<li><a href=\"https://reactjs.org/docs/hooks-reference.html#useimperativehandle\" class=\"link link-external\"><code>useImperativeHandle</code></a></li>\n</ul>\n</li>\n</ul>\n","summary":"A component wrapped in `React.forwardRef`\nnormally expects to receive a ref from its parent component.\nWhat if you want the component to have a ref\neven if it doesn't receive one from its parent?","date_published":"2022-10-19T12:00:00+03:00","tags":["JavaScript","React"]},{"id":"https://mtsknn.fi/blog/2-way-sync/","url":"https://mtsknn.fi/blog/2-way-sync/","title":"Software for syncing internal and external hard drives","content_html":"<p>I'm looking for a software that does 2-way syncing\nbetween my MacBook's hard drive and an external hard drive.\nSyncthing vs rsync vs Unison vs FreeFileSync vs Resilio Sync...\noh my!</p>\n<h2 id=\"my-use-case\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#my-use-case\">My use case</a></h2>\n<h3 id=\"problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problem\">Problem</a></h3>\n<p>I have important files (photos and such)\nscattered on three computers:</p>\n<ul>\n<li>A MacBook that I started using 9 months ago.</li>\n<li>A Lenovo laptop (Windows)\nthat I stopped using 9 months ago\nbecause I switched to the MacBook.</li>\n<li>A custom-built desktop computer (Windows)\nthat I stopped using 27 months ago\nbecause I switched to the Lenovo laptop.</li>\n</ul>\n<p>It's a mess!\nAnd I don't have back-ups. 🙈</p>\n<h3 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h3>\n<p>I want to:</p>\n<ol>\n<li>Copy important files on my current computer\nto an external hard drive.</li>\n<li>Copy/move important files on the two older computers\nto the same external hard drive.</li>\n<li>Sync the external hard drive and my current computer's hard drive.</li>\n</ol>\n<p>This way I'll have two copies of every important file:</p>\n<ul>\n<li>One copy on my current computer's hard drive.</li>\n<li>Another copy on my external hard drive.</li>\n</ul>\n<p>That's a good start!</p>\n<p>For this I'll need a software that does 2-way syncing.</p>\n<h2 id=\"comparing-software\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#comparing-software\">Comparing software</a></h2>\n<h3 id=\"syncthing-nope\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#syncthing-nope\">Syncthing – nope</a></h3>\n<p>I have never used <a href=\"https://syncthing.net/\" class=\"link link-external\">Syncthing</a>,\nbut I have heard it's good.\nKinda like Dropbox:</p>\n<blockquote>\n<p>Syncthing is a continuous file synchronization program.\nIt synchronizes files between two or more computers in real time [...].</p>\n</blockquote>\n<p>But Syncthing doesn't satisfy my needs at this point.\n<a href=\"https://docs.syncthing.net/users/faq.html#does-syncthing-support-syncing-between-folders-on-the-same-system\" class=\"link link-external\">From Syncthing's FAQ</a>:</p>\n<blockquote>\n<p><strong>Does Syncthing support syncing between folders on the same system?</strong></p>\n<p>No.\nSyncthing is not designed to sync locally\nand the overhead involved in doing so using Syncthing's method would be wasteful.\nThere are better programs to achieve this\nsuch as <a href=\"https://rsync.samba.org/\" class=\"link link-external\">rsync</a>\nor <a href=\"https://www.cis.upenn.edu/~bcpierce/unison\" class=\"link link-external\">Unison</a>.</p>\n</blockquote>\n<p><strong>Verdict:</strong> nope, can't use Syncthing in my case.</p>\n<h3 id=\"rsync-nope\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#rsync-nope\">rsync – nope</a></h3>\n<p>Quick googling reveals that\n<a href=\"https://stackoverflow.com/q/2936627/1079869\" class=\"link link-external\">rsync doesn't support 2-way syncing</a>.\nOr at least not in a straightforward way,\nand I'd like a simple solution for now.</p>\n<p>I need 2-way syncing\nbecause if I add files to the external hard drive from my old computers,\nI want the new files to be synced/copied to my current computer.</p>\n<p><strong>Verdict:</strong> nope, can't use rsync in my case.</p>\n<h3 id=\"unison-maybe\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#unison-maybe\">Unison – maybe</a></h3>\n<p><a href=\"https://www.cis.upenn.edu/~bcpierce/unison\" class=\"link link-external\">Unison</a>\nwas mentioned in Syncthing's FAQ\nand in the Stack Overflow page about rsync.\nAnd it indeed looks promising:</p>\n<ul>\n<li>Supports 2-way sync.</li>\n<li>Supports syncing between two (or more) folders on the same system.</li>\n<li><a href=\"https://news.ycombinator.com/item?id=28863974\" class=\"link link-external\">Unison can be used with a daemon or cron</a>,\nmaking future syncs automatic and easy.</li>\n</ul>\n<p>One caveat that I came across with:\n<a href=\"https://news.ycombinator.com/item?id=20632727\" class=\"link link-external\">Unison can't run on a &quot;heterogeneous network.&quot;</a>\nIn other words,\nUnison binaries on your different devices must be\n<a href=\"https://news.ycombinator.com/item?id=23540837\" class=\"link link-external\">&quot;exactly the same version, compiled with exactly the same version of various libraries,&quot;</a>\nor you'll run into problems.\nThis caveat doesn't apply to me for now\nsince I would be installing Unison only to my current computer,\nbut this is good to know\nand makes me wary of using Unison\n(what if I would later like to install it on another device?).</p>\n<p><strong>Verdict:</strong> Unison might fit the bill, but is there anything better?</p>\n<h3 id=\"freefilesync-my-choice\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#freefilesync-my-choice\">FreeFileSync – my choice</a></h3>\n<p><a href=\"https://freefilesync.org/\" class=\"link link-external\">FreeFileSync</a> apparently works quite much like Unison.</p>\n<p>I found someone saying on Hacker News that\n<a href=\"https://news.ycombinator.com/item?id=29662838\" class=\"link link-external\">FreeFileSync has better GUI than Unison</a>.\nSince googling &quot;FreeFileSync vs Unison&quot; doesn't yield many results,\nI might go ahead and trust that single internet stranger's word about the GUI.</p>\n<p><strong>Verdict:</strong> FreeFileSync should fit the bill and might be better than Unison.</p>\n<h3 id=\"resilio-sync-costs-money\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#resilio-sync-costs-money\">Resilio Sync – costs money</a></h3>\n<p><a href=\"https://www.resilio.com/individuals/\" class=\"link link-external\">Resilio Sync</a>\nis a &quot;fast, reliable, and simple file sync and share solution,\npowered by P2P technology.&quot;</p>\n<p>The Home edition costs $59.90 (one-time payment).\nThat's not much,\nbut I don't want to spend money on a syncing solution (at least not yet),\nso I'll skip Resilio Sync for now.\n(I don't even know if Resilio Sync would fit my needs.)</p>\n<p><strong>Verdict:</strong> Resilio Sync costs money, so I won't even consider it for now.</p>\n<h2 id=\"next-steps\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#next-steps\">Next steps</a></h2>\n<p>All right,\n<a href=\"https://freefilesync.org/\" class=\"link link-external\">FreeFileSync</a> is my choice.</p>\n<p>After I have used it to have two copies of my important files,\nI want to:</p>\n<ol start=\"4\">\n<li>Periodically (or preferably automatically)\nsync my current computer's hard drive and the external hard drive.</li>\n<li>Periodically (or preferably automatically)\nback up the files to an off-site location,\ne.g. to <a href=\"https://www.backblaze.com/b2/cloud-storage.html\" class=\"link link-external\">Backblaze B2</a>.</li>\n</ol>\n<p>This way I'll satisfy the 3-2-1 back-up strategy:</p>\n<ul>\n<li>Have at least 3 copies of data</li>\n<li>with 2 of the copies on different local devices</li>\n<li>and 1 copy in an off-site location.</li>\n</ul>\n<p>But I'm getting ahead of myself...\nI should start by installing FreeFileSync.</p>\n<p>Oh, but how about buying an external hard drive?\nYeah, I've already got one –\nI bought it 3 years ago for this purpose\nbut haven't even unboxed it yet. 🤡</p>\n","summary":"I'm looking for a software that does 2-way syncing\nbetween my MacBook's hard drive and an external hard drive.\nSyncthing vs rsync vs Unison vs FreeFileSync vs Resilio Sync...\noh my!","date_published":"2022-10-15T12:00:00+03:00","tags":["Cool tools"]},{"id":"https://mtsknn.fi/blog/git-stash-unstaged/","url":"https://mtsknn.fi/blog/git-stash-unstaged/","title":"Stashing only unstaged changes in Git","content_html":"<p><code>git stash -k</code> doesn't work as expected.\nUse a temporary commit instead.</p>\n<h2 id=\"non-solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#non-solution\">Non-solution</a></h2>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">git</span> stash -k  <span class=\"token comment\"># -k = --keep-index</span>\n</span></code></pre>\n<p>What this does:</p>\n<ul>\n<li>Keeps the staged changes: nice.</li>\n<li>Stashes unstaged <em>and staged</em> changes:\nnot nice since the stash entry will also contain the staged changes.\nThis can be problematic\nas (in this blog post) we want to stash only unstaged changes.</li>\n</ul>\n<h2 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h2>\n<p>Using <a href=\"https://github.com/mtsknn/dotfiles/blob/master/.gitconfig\" class=\"link link-external\">my Git aliases</a>:</p>\n<ol>\n<li><code>git c -m wip</code> to create a temporary commit</li>\n<li><code>git stash</code> to stash the remaining (unstaged/uncommitted) changes\n<ul>\n<li>Optionally use <code>-u</code> (= <code>--include-untracked</code>) to also stash untracked files</li>\n</ul>\n</li>\n<li><code>git undo</code> to <a href=\"/blog/how-to-undo-the-last-previous-most-recent-commit-in-git/\" class=\"link\">undo the previous commit</a>\n(the temporary commit done in step 1)\n<ul>\n<li>Optionally use <code>--soft</code> to keep the changes in the staging area</li>\n</ul>\n</li>\n</ol>\n<p>If not using my Git aliases:</p>\n<ul>\n<li><code>git c</code> = <code>git commit</code></li>\n<li><code>git undo</code> = <code>git reset HEAD~</code></li>\n</ul>\n<p>Source of the solution:\n<a href=\"https://stackoverflow.com/a/34681302/1079869\" class=\"link link-external\">Stephen Hanson's answer on Stack Overflow</a>.</p>\n<p>My point is that my Git aliases make the solution even nicer,\nespecially <code>git undo</code> since I can't remember the underlying command,\nplus <code>git reset</code> is scary (but the alias makes it less scary).</p>\n","summary":"`git stash -k` doesn't work as expected.\nUse a temporary commit instead.","date_published":"2022-10-12T12:00:00+03:00","tags":["Git"]},{"id":"https://mtsknn.fi/blog/coin-combinations-puzzle/","url":"https://mtsknn.fi/blog/coin-combinations-puzzle/","title":"JS solutions to the \"coin combinations\" interview puzzle","content_html":"<p>&quot;Given an array of coins and a target amount,\nreturn all combinations of coins that add up to the amount,\nor an empty array if no such combinations exist.&quot;</p>\n<h2 id=\"example-inputs-and-outputs\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-inputs-and-outputs\">Example inputs and outputs</a></h2>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">let</span> coins <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">]</span>\n<span class=\"token keyword\">let</span> amount <span class=\"token operator\">=</span> <span class=\"token number\">12</span>\n<span class=\"token function\">getCoinCombinations</span><span class=\"token punctuation\">(</span>coins<span class=\"token punctuation\">,</span> amount<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [[1, 1, 5, 5],  [1, 1, 10],  [2, 5, 5],  [2, 10]]</span>\n\ncoins <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">]</span>\namount <span class=\"token operator\">=</span> <span class=\"token number\">13</span>\n<span class=\"token function\">getCoinCombinations</span><span class=\"token punctuation\">(</span>coins<span class=\"token punctuation\">,</span> amount<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> []</span>\n</span></code></pre>\n<p>A slight variation to the puzzle is to return only one valid combination:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">let</span> coins <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">]</span>\n<span class=\"token keyword\">let</span> amount <span class=\"token operator\">=</span> <span class=\"token number\">12</span>\n<span class=\"token function\">getCoinCombination</span><span class=\"token punctuation\">(</span>coins<span class=\"token punctuation\">,</span> amount<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// Any of the following is valid:</span>\n<span class=\"token comment\">//=> [1, 1, 5, 5]</span>\n<span class=\"token comment\">//=> [1, 1, 10]</span>\n<span class=\"token comment\">//=> [2, 5, 5]</span>\n<span class=\"token comment\">//=> [2, 10]</span>\n</span></code></pre>\n<p>Let's solve both puzzles.</p>\n<h2 id=\"why-though\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-though\">Why though</a></h2>\n<p>It was fun to solve this!</p>\n<h2 id=\"initial-naive-solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#initial-naive-solution\">Initial naive solution</a></h2>\n<ol>\n<li>Find all possible coin combinations.\n<ul>\n<li>I have a separate post for that:\n<a href=\"/blog/js-array-item-combinations/\" class=\"link\"><em>Generating array permutations of all lengths in JS</em></a>.</li>\n</ul>\n</li>\n<li>Filter out the combinations whose sum doesn't match the target amount.</li>\n</ol>\n<h3 id=\"getting-all-combinations\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#getting-all-combinations\">Getting all combinations</a></h3>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> coins <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">]</span>\n<span class=\"token keyword\">const</span> amount <span class=\"token operator\">=</span> <span class=\"token number\">12</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCoinCombinations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">coins<span class=\"token punctuation\">,</span> amount</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  <span class=\"token function\">getCombinations</span><span class=\"token punctuation\">(</span>coins<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">sum</span><span class=\"token punctuation\">(</span>combo<span class=\"token punctuation\">)</span> <span class=\"token operator\">===</span> amount<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">sum</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">numbers</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> numbers<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> a <span class=\"token operator\">+</span> b<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// Via https://mtsknn.fi/blog/js-array-item-combinations/</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCombinations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">items</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  items<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> newCombos <span class=\"token operator\">=</span> combos<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> combo<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    combos<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>item<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>newCombos<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> combos\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h3 id=\"getting-a-single-combination\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#getting-a-single-combination\">Getting a single combination</a></h3>\n<p>Change <code>.filter()</code> to <code>.find()</code> and fall back to an empty array:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCoinCombination</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">coins<span class=\"token punctuation\">,</span> amount</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  <span class=\"token function\">getCombinations</span><span class=\"token punctuation\">(</span>coins<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">sum</span><span class=\"token punctuation\">(</span>combo<span class=\"token punctuation\">)</span> <span class=\"token operator\">===</span> amount<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n</span></code></pre>\n<h3 id=\"why-this-is-nice\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-this-is-nice\">Why this is nice</a></h3>\n<ul>\n<li>Straightforward and simple.</li>\n<li>No boring <code>for</code> loops.</li>\n</ul>\n<h3 id=\"why-this-is-naive\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-this-is-naive\">Why this is naive</a></h3>\n<ul>\n<li>\n<p>First all combinations are generated,\nthen they are looped through again to find matching combinations.\nThis is inefficient,\nespecially when getting only a single combination.</p>\n<ul>\n<li>\n<p>If <code>coins</code> is a large array (19+ items),\n<code>getCombinations()</code> gets very slow\nbecause the amount of total combinations (before filtering) will be <em>huge</em>.</p>\n<p>At some point (32+ coins) the code won't even work anymore\nbecause the unfiltered combinations won't fit in a single array.</p>\n<p>More details in a separate post:\n<a href=\"/blog/js-array-item-combinations-part-2/\" class=\"link\"><em>Finding array item combinations for large arrays is complex</em></a>.</p>\n</li>\n</ul>\n</li>\n<li>\n<p>When looping through the combinations to find matching combinations,\neach combination's sum is calculated.\nThe sums could be calculated earlier to avoid having to:</p>\n<ul>\n<li>loop through them again (in the <code>filter()</code> or <code>find()</code>).</li>\n<li>keep invalid combinations around.</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"better-solution-for-getting-all-combinations\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#better-solution-for-getting-all-combinations\">Better solution for getting all combinations</a></h2>\n<h3 id=\"starting-point\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#starting-point\">Starting point</a></h3>\n<p>Here's the initial solution\nbut without the separate <code>getCombinations</code> function:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCoinCombinations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">coins<span class=\"token punctuation\">,</span> amount</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  coins\n    <span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> coin</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> newCombos <span class=\"token operator\">=</span> combos<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> combo<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>coin<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n      combos<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>coin<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>newCombos<span class=\"token punctuation\">)</span>\n      <span class=\"token keyword\">return</span> combos\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">sum</span><span class=\"token punctuation\">(</span>combo<span class=\"token punctuation\">)</span> <span class=\"token operator\">===</span> amount<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">sum</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">numbers</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> numbers<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> a <span class=\"token operator\">+</span> b<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h3 id=\"step-1\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-1\">Step 1</a></h3>\n<p>Each combination in <code>combos</code> is an array of numbers (coins),\nand each combination's sum is calculated in the filter method.</p>\n<p>If the sums were calculated already in the reducer,\nthere would be no need to loop through each combination's coins in the filter method.</p>\n<p>This can be done by making each combination an object,\nlike <code>{ coins: [1, 2, 1, 5], sum: 9 }</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCoinCombinations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">coins<span class=\"token punctuation\">,</span> amount</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  coins\n    <span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> coin</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> newCombos <span class=\"token operator\">=</span> combos<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">coins</span><span class=\"token operator\">:</span> combo<span class=\"token punctuation\">.</span>coins<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>coin<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">sum</span><span class=\"token operator\">:</span> combo<span class=\"token punctuation\">.</span>sum <span class=\"token operator\">+</span> coin<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n      combos<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>\n        <span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">coins</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>coin<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token literal-property property\">sum</span><span class=\"token operator\">:</span> coin<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token operator\">...</span>newCombos\n      <span class=\"token punctuation\">)</span>\n\n      <span class=\"token keyword\">return</span> combos\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> combo<span class=\"token punctuation\">.</span>sum <span class=\"token operator\">===</span> amount<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> combo<span class=\"token punctuation\">.</span>coins<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>There's a new mapper function at the end,\nbut we'll deal with that soon.</p>\n<h3 id=\"step-2\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-2\">Step 2</a></h3>\n<p>Does every combination need to be stored in <code>combos</code>?</p>\n<p>Example:</p>\n<ul>\n<li>All coins: 1, 2, 1, 5, 5, 10.</li>\n<li>Target amount is 12.</li>\n<li>Current coin is the second last coin (value 5).</li>\n<li>A previous combination has the coins 1, 2, 1 and 5 (→ sum 9).</li>\n<li><strong>Question:</strong>\nDoes it make sense to create a new combination\nby adding the current coin (5) to the previous combination's coins?</li>\n<li><strong>Answer:</strong>\nThe new combination's sum would be 14,\nwhich is greater than the target amount (12),\nso no:\ncreating that combination wouldn't make sense.</li>\n</ul>\n<p>Changing <code>combos.map()</code> to <code>combos.forEach()</code>\nallows conditionally pushing one valid combination at a time to <code>combos</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCoinCombinations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">coins<span class=\"token punctuation\">,</span> amount</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  coins\n    <span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> coin</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      combos<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> newSum <span class=\"token operator\">=</span> combo<span class=\"token punctuation\">.</span>sum <span class=\"token operator\">+</span> coin\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>newSum <span class=\"token operator\">&lt;=</span> amount<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          combos<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n            <span class=\"token literal-property property\">coins</span><span class=\"token operator\">:</span> combo<span class=\"token punctuation\">.</span>coins<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>coin<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token literal-property property\">sum</span><span class=\"token operator\">:</span> newSum<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>coin <span class=\"token operator\">&lt;=</span> amount<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        combos<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">coins</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>coin<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token literal-property property\">sum</span><span class=\"token operator\">:</span> coin<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span>\n\n      <span class=\"token keyword\">return</span> combos\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> combo<span class=\"token punctuation\">.</span>sum <span class=\"token operator\">===</span> amount<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> combo<span class=\"token punctuation\">.</span>coins<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>There's some duplication\n(the codes inside <code>combos.forEach()</code> and below it look very similar),\nbut we'll deal with that soon.</p>\n<h3 id=\"step-3\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-3\">Step 3</a></h3>\n<p><code>combos</code> contains both incomplete combinations like <code>[2, 5]</code> (sum 7)\nand complete combinations like <code>[2, 5, 5]</code> (sum 12).</p>\n<p>If incomplete and complete combinations were stored separately,\nthe filtering after the reducer would be unnecessary.</p>\n<p>This can be done by changing the accumulator array (<code>combos</code>)\nto an object that contains two arrays (<code>{ complete: [], incomplete: [] }</code>).\nNo need to store sums of the complete combinations (the sums should be equal to <code>amount</code>),\nso no need for the mapper function after the reducer either:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCoinCombinations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">coins<span class=\"token punctuation\">,</span> amount</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  coins<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> coin</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      combos<span class=\"token punctuation\">.</span>incomplete<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> newSum <span class=\"token operator\">=</span> combo<span class=\"token punctuation\">.</span>sum <span class=\"token operator\">+</span> coin\n\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>newSum <span class=\"token operator\">></span> amount<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span>\n\n        <span class=\"token keyword\">const</span> newCoins <span class=\"token operator\">=</span> combo<span class=\"token punctuation\">.</span>coins<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>coin<span class=\"token punctuation\">)</span>\n\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>newSum <span class=\"token operator\">===</span> amount<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          combos<span class=\"token punctuation\">.</span>complete<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>newCoins<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token punctuation\">{</span>\n          combos<span class=\"token punctuation\">.</span>incomplete<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n            <span class=\"token literal-property property\">coins</span><span class=\"token operator\">:</span> newCoins<span class=\"token punctuation\">,</span>\n            <span class=\"token literal-property property\">sum</span><span class=\"token operator\">:</span> newSum<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>coin <span class=\"token operator\">===</span> amount<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        combos<span class=\"token punctuation\">.</span>complete<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>coin<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span> <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>coin <span class=\"token operator\">&lt;</span> amount<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        combos<span class=\"token punctuation\">.</span>incomplete<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">coins</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>coin<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n          <span class=\"token literal-property property\">sum</span><span class=\"token operator\">:</span> coin<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span>\n\n      <span class=\"token keyword\">return</span> combos\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">complete</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">incomplete</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>complete\n</span></code></pre>\n<h3 id=\"step-4\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-4\">Step 4</a></h3>\n<p>Final step is to reduce duplication:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCoinCombinations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">coins<span class=\"token punctuation\">,</span> amount</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> coins<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> coin</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      combos<span class=\"token punctuation\">.</span>incomplete<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">keepComboIfValid</span><span class=\"token punctuation\">(</span>combos<span class=\"token punctuation\">,</span> combo<span class=\"token punctuation\">.</span>coins<span class=\"token punctuation\">,</span> coin<span class=\"token punctuation\">,</span> combo<span class=\"token punctuation\">.</span>sum <span class=\"token operator\">+</span> coin<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n      <span class=\"token function\">keepComboIfValid</span><span class=\"token punctuation\">(</span>combos<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> coin<span class=\"token punctuation\">,</span> coin<span class=\"token punctuation\">)</span>\n\n      <span class=\"token keyword\">return</span> combos\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">complete</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">incomplete</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>complete\n\n  <span class=\"token keyword\">function</span> <span class=\"token function\">keepComboIfValid</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> previousCoins<span class=\"token punctuation\">,</span> coin<span class=\"token punctuation\">,</span> sum</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>sum <span class=\"token operator\">></span> amount<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span>\n    <span class=\"token keyword\">const</span> newCoins <span class=\"token operator\">=</span> previousCoins<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>coin<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>sum <span class=\"token operator\">===</span> amount<span class=\"token punctuation\">)</span> combos<span class=\"token punctuation\">.</span>complete<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>newCoins<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">else</span> combos<span class=\"token punctuation\">.</span>incomplete<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">coins</span><span class=\"token operator\">:</span> newCoins<span class=\"token punctuation\">,</span> sum <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p><code>keepComboIfValid</code> mutates the reducer function's accumulator object.\nBut I think that's OK\nbecause the mutations are restricted to the <code>getCoinCombinations</code> function,\nso the mutations are still <em>local</em>.\nRelated blog post:\n<a href=\"/blog/local-mutations/\" class=\"link\"><em>Local mutations are all right</em></a>.</p>\n<h4>Potential misstep</h4>\n<p>It would be tempting to simplify the <code>keepComboIfValid</code> function like this:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// ⛔️ Nope</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCoinCombinations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">coins<span class=\"token punctuation\">,</span> amount</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> coins<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> coin</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      combos<span class=\"token punctuation\">.</span>incomplete<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">keepComboIfValid</span><span class=\"token punctuation\">(</span>combos<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">coins</span><span class=\"token operator\">:</span> combo<span class=\"token punctuation\">.</span>coins<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>coin<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token literal-property property\">sum</span><span class=\"token operator\">:</span> combo<span class=\"token punctuation\">.</span>sum <span class=\"token operator\">+</span> coin<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n      <span class=\"token function\">keepComboIfValid</span><span class=\"token punctuation\">(</span>combos<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">coins</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>coin<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">sum</span><span class=\"token operator\">:</span> coin <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n      <span class=\"token keyword\">return</span> combos\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">complete</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">incomplete</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>complete\n\n  <span class=\"token keyword\">function</span> <span class=\"token function\">keepComboIfValid</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> combo</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>combo<span class=\"token punctuation\">.</span>sum <span class=\"token operator\">===</span> amount<span class=\"token punctuation\">)</span> combos<span class=\"token punctuation\">.</span>complete<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>combo<span class=\"token punctuation\">.</span>coins<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">else</span> <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>combo<span class=\"token punctuation\">.</span>sum <span class=\"token operator\">&lt;</span> amount<span class=\"token punctuation\">)</span> combos<span class=\"token punctuation\">.</span>incomplete<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>combo<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>However, this would be less efficient\nbecause new combinations are generated\nand then immediately discarded if they are not valid.</p>\n<h2 id=\"better-solution-for-getting-a-single-combination\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#better-solution-for-getting-a-single-combination\">Better solution for getting a single combination</a></h2>\n<p>Similar to the previous code,\nbut:</p>\n<ul>\n<li>No need to store complete combinations in an array;\nwe want to return the first complete combination immediately when it's found.</li>\n<li>A reducer function can't be terminated early,\nso we need to use <code>for</code> loops instead.</li>\n</ul>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCoinCombination</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">coins<span class=\"token punctuation\">,</span> amount</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> incompleteCombos <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n\n  <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> coin <span class=\"token keyword\">of</span> coins<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>coin <span class=\"token operator\">></span> amount<span class=\"token punctuation\">)</span> <span class=\"token keyword\">continue</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>coin <span class=\"token operator\">===</span> amount<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token punctuation\">[</span>coin<span class=\"token punctuation\">]</span>\n\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> length <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> incompleteCombos<span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> length<span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> combo <span class=\"token operator\">=</span> incompleteCombos<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span>\n      <span class=\"token keyword\">const</span> newSum <span class=\"token operator\">=</span> combo<span class=\"token punctuation\">.</span>sum <span class=\"token operator\">+</span> coin\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>newSum <span class=\"token operator\">></span> amount<span class=\"token punctuation\">)</span> <span class=\"token keyword\">continue</span>\n      <span class=\"token keyword\">const</span> newCoins <span class=\"token operator\">=</span> combo<span class=\"token punctuation\">.</span>coins<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>coin<span class=\"token punctuation\">)</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>newSum <span class=\"token operator\">===</span> amount<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> newCoins\n      incompleteCombos<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">coins</span><span class=\"token operator\">:</span> newCoins<span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">sum</span><span class=\"token operator\">:</span> newSum <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n\n    incompleteCombos<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">coins</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>coin<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">sum</span><span class=\"token operator\">:</span> coin <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h3 id=\"potential-misstep\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#potential-misstep\">Potential misstep</a></h3>\n<p>These wouldn't work for the inner <code>for</code> loop\nbecause we are pushing to <code>incompleteCombos</code> inside the loop:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> combo <span class=\"token keyword\">of</span> incompleteCombos<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> incompleteCombos<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Both of these would process the newly-pushed combinations,\nwhich would be incorrect.</p>\n<p>So, the length of <code>incompleteCombos</code> needs to be stored at the beginning of the looping.\nThese two are effectively the same,\nthe second one is just trying to be clever:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> length <span class=\"token operator\">=</span> incompleteCombos<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> length<span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">let</span> i <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> length <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> incompleteCombos<span class=\"token punctuation\">;</span> i <span class=\"token operator\">&lt;</span> length<span class=\"token punctuation\">;</span> i<span class=\"token operator\">++</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</span></code></pre>\n","summary":"\"Given an array of coins and a target amount,\nreturn all combinations of coins that add up to the amount,\nor an empty array if no such combinations exist.\"","date_published":"2022-10-10T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/local-mutations/","url":"https://mtsknn.fi/blog/local-mutations/","title":"Local mutations are all right","content_html":"<p>For example,\nmutating a reducer function's accumulator array can be better\nthan religiously avoiding mutations and returning only new arrays from the reducer.</p>\n<h2 id=\"example-of-a-mutating-code\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-of-a-mutating-code\">Example of a mutating code</a></h2>\n<p>I recently published a blog post about\n<a href=\"/blog/js-array-item-combinations/\" class=\"link\">how to find all possible combinations of array items in JS</a>.\nHere's the code:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCombinations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">items</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  items<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> newCombos <span class=\"token operator\">=</span> combos<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> combo<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    combos<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>item<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>newCombos<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> combos\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Notice how I'm mutating the accumulator array (<code>combos</code>) in the reducer function\nby pushing items to it?\nGasp!</p>\n<p>Mutations can easily lead to confusing code.\nHowever, not all mutations are bad.</p>\n<p>In this case,\nthe mutations are <em>local</em> –\nmutations of <code>combos</code> happen only inside the reducer function.\nKeeping track (mentally) of the mutations is easy.</p>\n<h2 id=\"avoiding-that-mutation\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#avoiding-that-mutation\">Avoiding that mutation</a></h2>\n<p>The mutations <em>could</em> be avoided e.g. like this:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCombinations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">items</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  items<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n      combos<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">[</span>item<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>combos<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> combo<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Look ma, no mutation!\n(<code>combos.concat()</code> returns a new array instead of mutating the existing array.)</p>\n<p>Or like this:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCombinations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">items</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  items<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">[</span>\n      <span class=\"token operator\">...</span>combos<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">[</span>item<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n      <span class=\"token operator\">...</span>combos<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> combo<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>But both non-mutating versions are actually horrible:</p>\n<h3 id=\"inefficient\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#inefficient\">Inefficient</a></h3>\n<p>A new accumulator array is created on each iteration.\nIt doesn't matter with short input arrays,\nbut it <em>does</em> matter with longer input arrays.</p>\n<p>In <a href=\"https://github.com/mtsknn/advent-of-code/blob/master/2015/03.md\" class=\"link link-external\">my solution to the Advent of Code 2015/03 puzzle</a>,\nI found that using the spread syntax made my code <em>a hundred times slower</em> (on my machine)\ncompared to mutating the accumulator array –\n10ms vs 1,000ms.\n(<a href=\"https://adventofcode.com/\" class=\"link link-external\">Advent of Code puzzles</a> typically have large inputs.)</p>\n<h3 id=\"less-readable\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#less-readable\">Less readable</a></h3>\n<p>In the first version (using <code>combos.concat()</code>),\nit's difficult to see at a glance what the non-mutating one-liner is doing.</p>\n<p>Prettier also insists on formatting the reducer function's body on a single line.\nThe following would be clearer (but still not as clear as the non-mutating version):</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCombinations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">items</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  items<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">(</span><span class=\"token parameter\">combos<span class=\"token punctuation\">,</span> item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n      <span class=\"token comment\">// prettier-ignore</span>\n      combos<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>\n        <span class=\"token punctuation\">[</span>item<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n        <span class=\"token operator\">...</span>combos<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">combo</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> combo<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>The second version (no <code>combos.concat()</code>) is better\nbut still not great (debatable, I guess).</p>\n<h3 id=\"unnecessary\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#unnecessary\">Unnecessary</a></h3>\n<p>The accumulator value is quite <em>meant</em> to be mutated –\nyou create an array or an object to which you accumulate values on every iteration.</p>\n<p>Saying that the accumulator is meant to be mutated is a bold statement,\nso I checked <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce\" class=\"link link-external\">what MDN says about array reducers in JS</a>\n(emphasis added by me):</p>\n<blockquote>\n<p><code>reduce()</code> is a central concept in <a href=\"https://en.wikipedia.org/wiki/Functional_programming\" class=\"link link-external\">functional programming</a>,\nwhere it's not possible to mutate any value,\nso in order to accumulate all values in an array,\none must return a new accumulator value on every iteration.\n<strong>This convention propagates to JavaScript's <code>reduce()</code>:\nyou should use <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax\" class=\"link link-external\">spreading</a>\nor other copying methods\nwhere possible\nto create new arrays and objects as the accumulator,\nrather than mutating the existing one.</strong> [...]</p>\n</blockquote>\n<p>Eh...\nI respect the MDN docs,\nbut I don't buy that.\nIf you are aiming for maximum purity etc.,\nthen maybe yeah.\nBut I would prefer performance and clarity to a rigid functional programming mindset.</p>\n<p>I wanted more opinions (:D),\nso I googled &quot;js reduce mutate accumulator&quot;\nand found <a href=\"https://stackoverflow.com/q/63727586/1079869\" class=\"link link-external\"><em>Is mutating accumulator in reduce function considered bad practice?</em> on Stack Overflow</a>.\nThe currently highest-scored answer by &quot;customcommander&quot; has a nice summary:</p>\n<blockquote>\n<p>It isn't [a bad practice] if you own the accumulator.</p>\n</blockquote>\n<p>And that's quite much what I mean by local mutations:</p>\n<ul>\n<li>If you own the accumulator,\nthe mutations are local (restricted to the reducer function's scope)\nand thus safe.</li>\n<li>If you don't own the accumulator –\nif it's coming from elsewhere –\nthe mutations won't be local anymore,\nso you need to be very careful with the mutations.</li>\n</ul>\n<p>If the accumulator is coming from elsewhere,\nit's easy to shallow-copy it to avoid non-local mutations:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">array<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">acc<span class=\"token punctuation\">,</span> val</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n    <span class=\"token keyword\">return</span> acc\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">{</span> <span class=\"token operator\">...</span>objectComingFromElsewhere <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span>\n\narray<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">acc<span class=\"token punctuation\">,</span> val</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n    <span class=\"token keyword\">return</span> acc\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token operator\">...</span>arrayComingFromElsewhere<span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>But be wary of deep mutations,\nor do a deep clone instead.</p>\n<h2 id=\"conclusion\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#conclusion\">Conclusion</a></h2>\n<p>I'll conclude by quoting the last two paragraphs\nof <a href=\"https://gist.github.com/customcommander/97eb4b3f1600773db59406d39f3f9cd7\" class=\"link link-external\">a relevant article</a>\nby the author of the aforementioned Stack Overflow answer (two commas added by me for clarity):</p>\n<blockquote>\n<p>Uncontrolled access to shared state can lead to complex bugs\nwhich is why immutability is so dear to functional programmers.\nHowever,\nimmutability isn't only achieved through the cloning of data,\nin fact this is most likely to be a naive approach to it.\nImmutability is a quality of a data structure,\nnot a process per se.</p>\n<p>In this particular case\nthe initial value of the reducing function is <em>safe</em> to mutate.\nThe spread operator offers no additional protection against side effects.</p>\n</blockquote>\n<p>In other words:\nlocal mutations are all right.</p>\n","summary":"For example,\nmutating a reducer function's accumulator array can be better\nthan religiously avoiding mutations and returning only new arrays from the reducer.","date_published":"2022-10-04T12:00:00+03:00","tags":["Clean code","JavaScript"]},{"id":"https://mtsknn.fi/blog/js-array-item-combinations-part-2/","url":"https://mtsknn.fi/blog/js-array-item-combinations-part-2/","title":"Generating permutations of large arrays is complex","content_html":"<p>That's because it would take a very long time,\nand at some point you would have so many permutations\nthat they wouldn't fit in an array.</p>\n<h2 id=\"starting-point\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#starting-point\">Starting point</a></h2>\n<p>By &quot;array permutations&quot; I'm referring to my blog post from yesterday:\n<a href=\"/blog/js-array-item-combinations/\" class=\"link\"><em>Generating array permutations with all lengths in JS</em></a>.</p>\n<p>Here's the code:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getPermutations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">items</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  items<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">permutations<span class=\"token punctuation\">,</span> item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> newPermutations <span class=\"token operator\">=</span> permutations<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">permutation</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n      permutation<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">)</span>\n    permutations<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>item<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>newPermutations<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> permutations\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [[1],  [2],  [1, 2],  [3],  [1, 3],  [2, 3],  [1, 2, 3]]</span>\n</span></code></pre>\n<h2 id=\"rangeerrors\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#rangeerrors\">RangeErrors</a></h2>\n<p>The code throws if the input array's length is too long.\nIn Firefox the limit is 20:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">19</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">20</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// RangeError</span>\n</span></code></pre>\n<ul>\n<li>In Firefox,\nthe error message is &quot;too many function arguments.&quot;</li>\n<li>In Chrome, Edge, Safari and Node.js,\nthe error message is &quot;Maximum call stack size exceeded.&quot;</li>\n</ul>\n<p>Firefox's error message seems more helpful\nsince the problem is that <code>permutations.push()</code> is called with too many arguments.\nIn Firefox the limit is 500,000 items:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">500_000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK</span>\n<span class=\"token punctuation\">;</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">500_001</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// RangeError</span>\n</span></code></pre>\n<h2 id=\"avoiding-rangeerrors\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#avoiding-rangeerrors\">Avoiding RangeErrors</a></h2>\n<p>We could try to avoid the problem by avoiding the spread syntax altogether:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getPermutations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">items</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token prefix unchanged\"> </span>  items<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">permutations<span class=\"token punctuation\">,</span> item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>    <span class=\"token keyword\">const</span> newPermutations <span class=\"token operator\">=</span> permutations<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">permutation</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token prefix deleted\">-</span>      permutation<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span>\n<span class=\"token prefix deleted\">-</span>    <span class=\"token punctuation\">)</span>\n<span class=\"token prefix deleted\">-</span>    permutations<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>item<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>newPermutations<span class=\"token punctuation\">)</span>\n</span>\n<span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>    permutations<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">permutation</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token prefix inserted\">+</span>      permutations<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>permutation<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix inserted\">+</span>    <span class=\"token punctuation\">)</span>\n<span class=\"token prefix inserted\">+</span>    permutations<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>item<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>    <span class=\"token keyword\">return</span> permutations\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span></span></code></pre>\n<p>(I thought that this would also increase performance,\nbut it doesn't seem to.\nMakes sense though:\nwe do avoid iterating through <code>newPermutations</code> which is good,\nbut now we are calling <code>permutations.push()</code> many more times with only one argument.)</p>\n<h2 id=\"its-slow\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#its-slow\">It's slow</a></h2>\n<p>Now larger input arrays work,\nbut getting permutations for large input arrays is increasingly slow:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">console<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">20</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> ~200ms (on my machine)</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">21</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> ~400ms (on my machine)</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">22</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> ~800ms (on my machine)</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">23</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> ~1800ms (on my machine)</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">24</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> ~4200ms (on my machine)</span>\n</span></code></pre>\n<p>Ain't nobody got time to wait that long!</p>\n<h2 id=\"the-input-arrays-length-is-still-limited\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-input-arrays-length-is-still-limited\">The input array's length is still limited</a></h2>\n<p>In Firefox,\nthe maximum length of an array seems to be <code>2 ** 32 - 1</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span> <span class=\"token operator\">**</span> <span class=\"token number\">32</span> <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK</span>\n<span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span> <span class=\"token operator\">**</span> <span class=\"token number\">32</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// RangeError: invalid array length</span>\n</span></code></pre>\n<p>The length of the array returned by <code>getPermutations(items)</code>\nis incidentally <code>2 ** items.length - 1</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">1</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length <span class=\"token operator\">===</span> <span class=\"token number\">2</span> <span class=\"token operator\">**</span> <span class=\"token number\">1</span> <span class=\"token operator\">-</span> <span class=\"token number\">1</span> <span class=\"token comment\">// true</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">2</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length <span class=\"token operator\">===</span> <span class=\"token number\">2</span> <span class=\"token operator\">**</span> <span class=\"token number\">2</span> <span class=\"token operator\">-</span> <span class=\"token number\">1</span> <span class=\"token comment\">// true</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">3</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length <span class=\"token operator\">===</span> <span class=\"token number\">2</span> <span class=\"token operator\">**</span> <span class=\"token number\">3</span> <span class=\"token operator\">-</span> <span class=\"token number\">1</span> <span class=\"token comment\">// true</span>\n<span class=\"token comment\">// ...</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">10</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length <span class=\"token operator\">===</span> <span class=\"token number\">2</span> <span class=\"token operator\">**</span> <span class=\"token number\">10</span> <span class=\"token operator\">-</span> <span class=\"token number\">1</span> <span class=\"token comment\">// true</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">11</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length <span class=\"token operator\">===</span> <span class=\"token number\">2</span> <span class=\"token operator\">**</span> <span class=\"token number\">11</span> <span class=\"token operator\">-</span> <span class=\"token number\">1</span> <span class=\"token comment\">// true</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">12</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length <span class=\"token operator\">===</span> <span class=\"token number\">2</span> <span class=\"token operator\">**</span> <span class=\"token number\">12</span> <span class=\"token operator\">-</span> <span class=\"token number\">1</span> <span class=\"token comment\">// true</span>\n<span class=\"token comment\">// ...and so on</span>\n</span></code></pre>\n<p>(<a href=\"https://math.stackexchange.com/q/1117236/18013\" class=\"link link-external\">Math for calculating the total number of permutations.</a>)</p>\n<p>In other words,\nthe maximum length of the input array is 32.</p>\n<p>But input arrays that long would be impractical\nsince getting all permutations would take way too long.\nPlus there would be quite many permutations:\n<code>2 ** 32 - 1</code> = 4,294,967,295.\nOver 4 billion!</p>\n<h2 id=\"think-of-alternatives\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#think-of-alternatives\">Think of alternatives</a></h2>\n<p>Okay,\nso the input array's length is limited to 32 (in Firefox at least),\nand getting all permutations would take a very long time,\nso think:</p>\n<ul>\n<li>Do you need to generate all permutations?</li>\n<li>Do you need to retain all permutations?</li>\n</ul>\n<p>Some pointers:</p>\n<ul>\n<li>If you only need permutations that are e.g. 2 or 3 items long,\nthere's no point in generating longer permutations.</li>\n<li>If you are looking for a single permutation that matches a certain criterion,\nterminate the process after finding your solution.\n<ul>\n<li>Not possible if using an array reducer;\nuse <code>for</code> loops and the <code>break</code> statement instead.</li>\n</ul>\n</li>\n<li>If you are looking for multiple permutations that match a certain criterion,\nskip non-matching permutations early,\ni.e. don't store them in the accumulator array.\n<ul>\n<li>Example:\n<a href=\"/blog/coin-combinations-puzzle/\" class=\"link\"><em>JS solutions to the &quot;coin combinations&quot; interview puzzle</em></a>.</li>\n</ul>\n</li>\n<li>I don't remember if I have ever used generator functions,\nbut this could be a good use case for them.\nI got this idea from <a href=\"https://evanhahn.com/nested-list-permutations-in-javascript/\" class=\"link link-external\">Evan Hahn's blog post <em>Nested array permutations in JavaScript</em></a>.</li>\n</ul>\n","summary":"That's because it would take a very long time,\nand at some point you would have so many permutations\nthat they wouldn't fit in an array.","date_published":"2022-10-03T12:00:00+03:00","date_modified":"2023-11-02T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/js-array-item-combinations/","url":"https://mtsknn.fi/blog/js-array-item-combinations/","title":"Generating array permutations of all lengths in JS","content_html":"<p>Example:\nconverting <code>[1, 2, 3]</code>\ninto <code>[[1],  [2],  [1, 2],  [3],  [1, 3],  [2, 3],  [1, 2, 3]]</code>.</p>\n<h2 id=\"why\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why\">Why?</a></h2>\n<p>I recall needing this a few times,\nmaybe when solving <a href=\"https://adventofcode.com/\" class=\"link link-external\">Advent of Code puzzles</a>.</p>\n<p>Plus I really like my solution.\nI find it straightforward,\nmaybe even obvious now.\nBut it took me a while to come up with it –\nmy first attempts were much more complex and didn't even work properly –\nand maybe that's why I'm keen on this simple solution.</p>\n<h2 id=\"example-input-and-output\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-input-and-output\">Example input and output</a></h2>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> array <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token number\">4</span><span class=\"token punctuation\">]</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>array<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">/* =>\n  [\n    [1],\n    [2],\n    [1, 2],\n    [3],\n    [1, 3],\n    [2, 3],\n    [1, 2, 3],\n    [4],\n    [1, 4],\n    [2, 4],\n    [1, 2, 4],\n    [3, 4],\n    [1, 3, 4],\n    [2, 3, 4],\n    [1, 2, 3, 4],\n  ]\n*/</span>\n</span></code></pre>\n<h2 id=\"code\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#code\">Code</a></h2>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getPermutations</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">items</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  items<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">permutations<span class=\"token punctuation\">,</span> item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> newPermutations <span class=\"token operator\">=</span> permutations<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">permutation</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n      permutation<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">)</span>\n    permutations<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>item<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>newPermutations<span class=\"token punctuation\">)</span>\n    returnpermutations\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Probably not the most efficient solution,\nbut straightforward and easy.\nAnd no <code>for</code> loops!</p>\n<h2 id=\"order-of-permutations\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#order-of-permutations\">Order of permutations</a></h2>\n<p>The order of the final permutations change\nif you change the order of the <code>push</code> method's arguments:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> items <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">]</span>\n\nitems<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">permutations<span class=\"token punctuation\">,</span> item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> newPermutations <span class=\"token operator\">=</span> permutations<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">permutations</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n    permutations<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n  permutations<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>item<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>newPermutations<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> permutations\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [[1],  [2],  [1, 2],  [3],  [1, 3],  [2, 3],  [1, 2, 3]]</span>\n\n<span class=\"token comment\">// versus</span>\n\nitems<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">permutations<span class=\"token punctuation\">,</span> item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> newPermutations <span class=\"token operator\">=</span> permutations<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">permutations</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n    permutations<span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>item<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n  permutations<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span>newPermutations<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>item<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> permutations\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [[1],  [1, 2],  [2],  [1, 3],  [1, 2, 3],  [2, 3],  [3]]</span>\n</span></code></pre>\n<p>In some cases the order might matter.</p>\n<h2 id=\"beware-too-large-input-arrays\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#beware-too-large-input-arrays\">Beware too large input arrays</a></h2>\n<p>The code doesn't work if the input array's length is 20+:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">19</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK</span>\n<span class=\"token function\">getPermutations</span><span class=\"token punctuation\">(</span>Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">20</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// RangeError</span>\n</span></code></pre>\n<p>More details in Part 2:\n<a href=\"/blog/js-array-item-combinations-part-2\" class=\"link\"><em>Generating permutations of large arrays is complex</em></a>.</p>\n<h2 id=\"mutation\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#mutation\">Mutation</a></h2>\n<p>Notice how I'm mutating the accumulator array (<code>permutations</code>)\nin the reducer function?</p>\n<p>Mutations can be bad,\nbut in this case they are okay,\nbecause <a href=\"/blog/local-mutations/\" class=\"link\">local mutations are all right</a>.</p>\n","summary":"Example:\nconverting `[1, 2, 3]`\ninto `[[1],  [2],  [1, 2],  [3],  [1, 3],  [2, 3],  [1, 2, 3]]`.","date_published":"2022-10-02T12:00:00+03:00","date_modified":"2023-05-01T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/unreplied/","url":"https://mtsknn.fi/blog/unreplied/","title":"I want a chat app with an \"unreplied\" message state","content_html":"<p>Most chat apps have an &quot;unread&quot; message state\nto keep track of messages I haven't read yet.\nWhy isn't there a different state for messages I haven't replied to yet?</p>\n<p>I chat with <a href=\"https://hanki.dev/\" class=\"link link-external\">my bro</a> a lot.\nWe always have several conversations ongoing at the same time.</p>\n<p>We currently use Telegram,\nso all messages are in a single big list.</p>\n<p>When replying to his messages,\nI basically have two options:</p>\n<ul>\n<li>Reply to all unreplied messages in a single session.\nI don't like this because:\n<ul>\n<li>It would often take a very long times,\nsometimes even several hours (like I said, we chat a lot!).</li>\n<li>Sometimes it's better to ponder things before replying.</li>\n</ul>\n</li>\n<li>Reply to some messages now and some messages later.\nI don't like this because:\n<ul>\n<li>It gets very difficult to keep track of messages I have or haven't replied to.</li>\n</ul>\n</li>\n</ul>\n<p>And sometimes I want to start a new conversation\nand not reply to any previous messages.\nThis alone makes keeping track of replied/unreplied messages difficult.</p>\n<p>We have previously tried a private Discord server with several channels –\none main topic per channel, e.g. &quot;dev&quot; or &quot;health.&quot;\nHaving several channels helps a bit, but the core problem is still the same:\nit's difficult to keep track of replied/unreplied messages <em>in each channel</em>.</p>\n<p>One level deep sub-threads (like on Discord or Slack) don't help either:\nit's difficult to keep track of replied/unreplied messages <em>in each sub-thread</em>.\nSub-threads might even make things more confusing.</p>\n<p>A tree-like message structure (like on Reddit or Hacker News) could help organize messages\nbecause not all messages would be in a single big list (or several big lists if using channels),\nbut it wouldn't solve the problem of keeping track of replied/unreplied messages.\nPlus maybe a tree-like structure would get in the way of quick, comfortable chatting.</p>\n<p>So here's what I want:</p>\n<ul>\n<li>An &quot;unreplied&quot; message state for keeping track of replied/unreplied messages.</li>\n<li>Threads for organizing messages by conversation.\n<ul>\n<li>New conversation = new thread.</li>\n<li>Flat threads (no nesting, i.e. no sub-threads or tree-like structure)\nto keep things simple.</li>\n<li>Threads are superior to channels:\nno need to think of the most suitable channel/topic for a new conversation;\njust start a new thread.</li>\n</ul>\n</li>\n</ul>\n<p>Maybe I should create my own chat app. 🤷‍♂️</p>\n","summary":"Most chat apps have an \"unread\" message state\nto keep track of messages I haven't read yet.\nWhy isn't there a different state for messages I haven't replied to yet?","date_published":"2022-09-28T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/ltex/","url":"https://mtsknn.fi/blog/ltex/","title":"LTeX: grammar/spell checker on steroids for VS Code","content_html":"<p>Doesn't only look for typos\nbut for grammar and style issues as well.\nNot to be confused with LaTeX.</p>\n<h2 id=\"about-ltex\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#about-ltex\">About LTeX</a></h2>\n<p><a href=\"https://marketplace.visualstudio.com/items?itemName=valentjn.vscode-ltex\" class=\"link link-external\">LTeX</a> is a grammar/spell checker for VS Code.\nIt uses <a href=\"https://languagetool.org/\" class=\"link link-external\">LanguageTool</a> under the hood\nand supports LaTeX, Markdown and more.</p>\n<p>I have been using LTeX for half a year now.\nIt's great!</p>\n<p>Note that LTeX is not the same as <a href=\"https://www.latex-project.org/\" class=\"link link-external\">LaTeX, the &quot;document preparation system.&quot;</a></p>\n<p><a href=\"https://marketplace.visualstudio.com/items?itemName=valentjn.vscode-ltex\" class=\"link link-external\">Check out the LTeX extension for VS Code</a></p>\n<h2 id=\"pros\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#pros\">Pros</a></h2>\n<ul>\n<li>Checks for spelling issues (typos)</li>\n<li>Checks for grammar issues</li>\n<li>Checks for style issues\n<ul>\n<li>Example:\n<em>&quot;Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.&quot;</em>\nI get this often when starting too many sentences with the word &quot;I&quot;\n(maybe I love myself too much)</li>\n</ul>\n</li>\n<li>Explains why things are (or might be) wrong instead of just complaining</li>\n<li>Works offline</li>\n<li>Is privacy-friendly (because works offline)</li>\n<li>Is easy to set up:\ninstall the VS Code extension\nand the rest will be auto-installed</li>\n<li>Provides quick fixes (<kbd>Meta</kbd>+<kbd>.</kbd>)</li>\n<li>Doesn't slow down VS Code\n(at least not on my machine;\nsome other spell checker extensions have been horribly slow)</li>\n<li>Works even if you split sentences to multiple lines\n<ul>\n<li>Related: <a href=\"/blog/4-1-wrapping-styles-for-markdown-prose-and-code-comments/\" class=\"link\"><em>4 + 1 wrapping styles for Markdown prose and code comments</em></a></li>\n</ul>\n</li>\n</ul>\n<h2 id=\"cons\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#cons\">Cons</a></h2>\n<ul>\n<li>Doesn't support Finnish</li>\n<li>Doesn't support disabling checking for a single document/file\n<ul>\n<li>There's <em>&quot;LTeX: Clear Diagnostics in Current Document&quot;</em>\nin VS Code's Command Palette,\nbut the extension runs again when you type or erase even a single letter</li>\n</ul>\n</li>\n<li>Sometimes <a href=\"https://github.com/valentjn/vscode-ltex/issues/497\" class=\"link link-external\">leaves stray Java processes behind</a>\n<ul>\n<li>It might be a good idea to manually terminate the stray processes from time to time,\nespecially if you are low on RAM</li>\n</ul>\n</li>\n</ul>\n","summary":"Doesn't only look for typos\nbut for grammar and style issues as well.\nNot to be confused with LaTeX.","date_published":"2022-09-20T12:00:00+03:00","tags":["Cool tools","English","VS Code"]},{"id":"https://mtsknn.fi/blog/linen-bedclothes/","url":"https://mtsknn.fi/blog/linen-bedclothes/","title":"Linen bedclothes are the best","content_html":"<p>I recently switched from linen bedclothes (sheets, pillowcases, duvet covers)\nback to cotton bedclothes –\nbad idea.\nMy sleep quality took a drastic hit.</p>\n<p>Sometimes you notice the importance of something only after losing that thing.\nThis is such a case.<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup></p>\n<p>I have been using linen bedclothes for almost two years.</p>\n<p>Recently I started using a second set of bedclothes to make laundry days easier –\nno need to wash the bedclothes first thing in the morning if I have more than one set of bedclothes.<sup><a aria-label=\"footnote 2\" class=\"link\" href=\"#fn-2\" id=\"fnref-2\">[2]</a></sup></p>\n<p>However, the second set of bedclothes is made of cotton.\nI didn't remember how bad cotton bedclothes are:</p>\n<ul>\n<li>Cotton doesn't breathe properly.</li>\n<li>Cotton feels &quot;hard&quot; (not soft).</li>\n</ul>\n<p>It's possible that not all cotton bedclothes are bad,\nbut mine are.</p>\n<p>Last night I slept again with my linen bedclothes – and boy are they <em>wonderful</em>!\nI'll ditch the cotton bedclothes as soon as I have ordered a second set of linen bedclothes\n(so hopefully before next year).</p>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>I didn't lose my linen bedclothes.\nI lost my higher quality sleep for a while. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n<li id=\"fn-2\"><p>It's not a big deal to wash the bedclothes first thing in the morning,\nbut if you forget and do it later in the day,\nthey might not get dry enough before the night. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-2\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"I recently switched from linen bedclothes (sheets, pillowcases, duvet covers)\nback to cotton bedclothes –\nbad idea.\nMy sleep quality took a drastic hit.","date_published":"2022-09-17T12:00:00+03:00","tags":["Sleep"]},{"id":"https://mtsknn.fi/blog/netlify-updating-private-git-submodule/","url":"https://mtsknn.fi/blog/netlify-updating-private-git-submodule/","title":"Updating a private Git submodule before builds in Netlify","content_html":"<p>Automatically updating a private Git submodule before builds is convenient\nbut requires some setup.</p>\n<h2 id=\"why-a-private-git-submodule\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-a-private-git-submodule\">Why a private Git submodule</a></h2>\n<p><a href=\"https://github.com/mtsknn/mtsknn.fi\" class=\"link link-external\">My website is open source</a>,\nbut the blog posts and other content reside in a separate, private Git repo.</p>\n<p>Tania Rascia explains her rationale for her similar setup in her blog post\n<a href=\"https://www.taniarascia.com/git-submodules-private-content/\" class=\"link link-external\"><em>Using Git Submodules for Private Content</em></a>:</p>\n<blockquote>\n<p>My website has been open source for as long as it has existed.\nOriginally, it was a WordPress site,\nbut only the layout was out there for everyone to see\nsince the data was saved in a database.\nOnce I moved to Gatsby,\nI kept all the images and posts in a <code>content</code> directory.\nThis was way better,\nas my content is all conveniently stored in one easy-to-save folder\nand the posts are all in beautiful markdown.</p>\n<p>However,\npeople often like my layout and want to use it,\nso they clone and deploy this site.\nSometimes they will just leave up all the posts and images and update the name and image.\nAlthough I subscribe to the <a href=\"https://zenhabits.net/uncopyright/\" class=\"link link-external\">Zenhabits Uncopyright</a> philosophy towards content –\nmy content is out there for the world to see and do what they want with it, and it doesn't bother me –\nI don't think I should make it <em>quite</em> so easy to just clone everything I've written in a moment.\nIf you're going to plagiarize,\nyou should at least have to do a bit of work.</p>\n</blockquote>\n<p>Another benefit:\nI can store my sloppy drafts and scheduled posts in my private Git repo\nso that no one can see them (except me of course).</p>\n<h2 id=\"why-automatic-updating\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-automatic-updating\">Why automatic updating</a></h2>\n<p>From the summary of the aforementioned blog post by Tania Rascia:</p>\n<blockquote>\n<p>My current process for updating the site looks like:</p>\n<ol>\n<li>Make changes to <code>content</code> repo.</li>\n<li>Commit changes to <code>content</code> and push to private submodule hosted on GitHub:\n<code>git commit &amp;&amp; git push</code>.</li>\n<li>Pull new updates into local <code>taniarascia.com</code> repo:\n<code>git submodule update --remote</code>.</li>\n<li>Commit the new submodule changes and push to public GitHub repo:\n<code>git commit &amp;&amp; git push</code>.</li>\n<li>Netlify deploys the new site.</li>\n</ol>\n</blockquote>\n<p>That's cool,\nbut <strong>I don't want to update my website repo after modifying my content repo.</strong>\nMy process looks like this:</p>\n<ol>\n<li>Same as above.\n(Make changes to <code>content</code> repo.)</li>\n<li>Same as above.\n(Commit changes to <code>content</code> and push to private submodule hosted on GitHub:\n<code>git commit &amp;&amp; git push</code>.)</li>\n<li>Netlify deploys the new site automatically on the next day\nbecause I have set up\n<a href=\"/blog/how-to-trigger-daily-netlify-builds-using-github-actions/\" class=\"link\">automatic daily Netlify builds using GitHub Actions</a>.\nAlternatively I can manually trigger a build on the Netlify dashboard\nif I want the content changes to go live immediately (in a few minutes).</li>\n</ol>\n<p>There are two benefits to this way of working:</p>\n<ul>\n<li>I need to work with only one Git repo (the content repo) to write content.</li>\n<li>I can avoid the frequent &quot;update content submodule&quot; commits in my website repo.</li>\n</ul>\n<p>My website repo points to a commit from 1st Jan 2021 on the content repo,\nmeaning that I haven't needed an &quot;update content submodule&quot; commit on the website repo\nin almost two years.\nThis is possible because\nNetlify automatically updates the private Git submodule (i.e. pulls the latest changes)\nbefore it builds the site.</p>\n<h2 id=\"how-to\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#how-to\">How-to</a></h2>\n<p>First,\n<a href=\"https://www.taniarascia.com/git-submodules-private-content/\" class=\"link link-external\">check out the aforementioned blog post by Tania Rascia</a>.\nIt's a good primer on the subject.</p>\n<p>When you have a private Git submodule set up,\nhere are pointers how to make Netlify automatically update it before builds.\nThese pointers are based on <a href=\"https://answers.netlify.com/t/support-guide-using-an-ssh-key-via-environment-variable-during-build/2457\" class=\"link link-external\">a support guide on Netlify Support Forums</a>,\nbut I had to trial and error quite a bit before I got it all working.</p>\n<h3 id=\"deploy-key-ssh-key\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#deploy-key-ssh-key\">Deploy key + SSH key</a></h3>\n<p>You need to add <em>two</em> keys on your private Git repo's settings page on GitHub:</p>\n<ul>\n<li>Netlify deploy key:\nthis is needed so that Netlify can <em>initially access</em> the private submodule.\nCheck the blog post by Tania Rascia for a guide.</li>\n<li>A new SSH public key:\nthis is needed so that Netlify can <em>update</em> the private submodule (i.e. pull the latest changes)\nbefore it starts building the site.</li>\n</ul>\n<p>Steps:</p>\n<ol>\n<li><a href=\"https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent\" class=\"link link-external\">Generate a new SSH key</a>\n<ul>\n<li>Example on Mac: <code>ssh-keygen -t ed25519 -C &quot;yoursite.com content repo / Netlify&quot;</code></li>\n<li>The <code>-C</code> flag stands for &quot;comment&quot; –\nit's for you so you later know/remember what this SSH key is for</li>\n<li>Save the file e.g. to <code>/Users/you/.ssh/yoursite.com-content-netlify</code></li>\n<li>Don't set a password or Netlify can't update the submodule</li>\n</ul>\n</li>\n<li>Add the <strong>public</strong> key (the shorter one) as a read-only deploy key\non your private repo's settings page on GitHub\n<ul>\n<li>Name it e.g. &quot;SSH public key (Netlify)&quot;\nso you can tell it apart from the other deploy key (&quot;Netlify deploy key&quot;)</li>\n</ul>\n</li>\n</ol>\n<p>Note:</p>\n<ul>\n<li>After changing the build image on Netlify\n(the previous one I was using was going to be deprecated),\nthe Netlify deploy key changed too,\nso I had to add the new deploy key to the private Git repo's settings on GitHub.\n(I'm not 100% sure about this.)</li>\n</ul>\n<h3 id=\"environment-variable\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#environment-variable\">Environment variable</a></h3>\n<p>Add the <strong>private</strong> SSH key (the longer one)\nto your Netlify site's environment variables.\nBefore that though you need to modify the key\nbecause environment variables are single-line (not multi-line).</p>\n<p>Your private key looks something like this:</p>\n<pre aria-label=\"Code block\" class=\"language-text bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">-----BEGIN OPENSSH PRIVATE KEY-----\nn4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is\n...\n-----END OPENSSH PRIVATE KEY-----\n</span></code></pre>\n<p>Replace the newlines with underscores (e.g. in VS Code or Notepad)\nso that it looks like this:</p>\n<pre aria-label=\"Code block\" class=\"language-text bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">-----BEGIN OPENSSH PRIVATE KEY-----_n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is_..._-----END OPENSSH PRIVATE KEY-----_\n</span></code></pre>\n<p>Note: it doesn't matter whether the private key has a trailing underscore or not.</p>\n<h3 id=\"bash-script\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#bash-script\">Bash script</a></h3>\n<p>To let Netlify use the SSH key from the environment variables,\ncreate a file called e.g. <code>netlify-setup.sh</code>\nand save it to the website repo's root:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token shebang important\">#!/usr/bin/env bash</span>\n\n<span class=\"token comment\"># Credit goes to fool and hongaar at</span>\n<span class=\"token comment\"># https://community.netlify.com/t/support-guide-using-an-ssh-key-via-environment-variable-during-build/2457</span>\n\n<span class=\"token comment\"># Check if we're running in a Netlify environment</span>\n<span class=\"token comment\"># See https://www.netlify.com/docs/continuous-deployment/#environment-variables</span>\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">[</span> <span class=\"token operator\">!</span> -z <span class=\"token string\">\"<span class=\"token variable\">${DEPLOY_PRIME_URL}</span>\"</span> <span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">then</span>\n    <span class=\"token comment\"># Init .ssh dir and expand $SSH_KEY</span>\n    <span class=\"token function\">mkdir</span> -p ~/.ssh\n    <span class=\"token builtin class-name\">echo</span> -e <span class=\"token string\">\"<span class=\"token variable\">${SSH_KEY<span class=\"token operator\">/</span><span class=\"token operator\">/</span>_<span class=\"token operator\">/</span>\\\\n}</span>\"</span> <span class=\"token operator\">></span> ~/.ssh/id_rsa\n    <span class=\"token function\">chmod</span> og-rwx ~/.ssh/id_rsa\n\n    <span class=\"token comment\"># Uncomment to debug</span>\n    <span class=\"token comment\"># ls -la ~/.ssh</span>\n    <span class=\"token comment\"># cat ~/.ssh/id_rsa</span>\n\n    <span class=\"token comment\"># Add host keys</span>\n    ssh-keyscan -H github.com <span class=\"token operator\">>></span> ~/.ssh/known_hosts\n<span class=\"token keyword\">fi</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n<h3 id=\"build-command\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#build-command\">Build command</a></h3>\n<p>Last step!\nModify your build command (e.g. <code>npm run build</code>)\nto first run the Bash script,\nthen update the private Git submodule,\nand then build the site.</p>\n<p>There might be several ways to do this.\nI did it by specifying this as my build command in the <code>netlify.toml</code> file:</p>\n<style>\n  /*\n    Heh, Tailwind's `table` class\n    applied `display: table`\n    to one token in the code block below\n  */\n  .token.table {\n    display: inline;\n  }\n</style>\n<pre aria-label=\"Code block\" class=\"language-toml bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">[</span><span class=\"token table class-name\">build</span><span class=\"token punctuation\">]</span>\n  <span class=\"token key property\">command</span> <span class=\"token punctuation\">=</span> <span class=\"token string\">\"\"\"\n    bash ./netlify-setup.sh &amp;&amp;\n    GIT_SSH_COMMAND=\"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no\" \\\n      git submodule update --init --remote &amp;&amp;\n    npm run build\n  \"\"\"</span>\n</span></code></pre>\n","summary":"Automatically updating a private Git submodule before builds is convenient\nbut requires some setup.","date_published":"2022-09-15T12:00:00+03:00","tags":["Git","Netlify"]},{"id":"https://mtsknn.fi/blog/mortgage-calculator/","url":"https://mtsknn.fi/blog/mortgage-calculator/","title":"Mortgage calculations in JS: annuity and fixed principal","content_html":"<p>Instead of spending 5 minutes to look for and use an existing mortgage calculator,\nI wasted time figuring out how to do the calculations from scratch.</p>\n<h2 id=\"annuity-vs-fixed-principal\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#annuity-vs-fixed-principal\">Annuity vs fixed principal</a></h2>\n<ul>\n<li>In annuity the monthly payment is fixed (until the interest rate changes).</li>\n<li>In fixed principal the monthly payment gets smaller after each month;\nthe first payment is the largest\nand larger than in annuity.</li>\n<li>Fixed principal is cheaper over the whole mortgage period (less total interest fees),\nbut annuity is more affordable in the short term.</li>\n</ul>\n<p>I recall reading recently that annuity is used in like 99% of mortgages in Finland.\nI can't find that article anymore though,\nso I don't know if it's actually 99% or more like 90%.</p>\n<h2 id=\"btw-mathjax\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#btw-mathjax\">Btw, MathJax</a></h2>\n<p>Enable <a href=\"https://www.mathjax.org/\" class=\"link link-external\">MathJax</a> by clicking the button below\nfor a better reading experience.\nIt's opt-in because it's a largish download (300+ KB).</p>\n<style>\n  /* <kbd> styles */\n  #enable-mathjax {\n    background-color: #f1f5f9;\n    border: 1px solid #97a6ba;\n    border-radius: 8px;\n    box-shadow: 0 1px 0 0 #97a6ba, inset 0 2px 0 0 #fff;\n    display: block;\n    margin: 0 auto;\n    padding: 4px 8px;\n  }\n  #enable-mathjax:hover {\n    background-color: #fff;\n  }\n  #enable-mathjax:active {\n    background-color: #f1f5f9;\n  }\n  #enable-mathjax:focus {\n    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);\n    outline: transparent dotted 2px;\n  }\n\n  mjx-container {\n    overflow-x: auto;\n    padding: 2px 0;\n  }\n</style>\n<p><button id=\"enable-mathjax\">Load MathJax lib (300+ KB)</button></p>\n<script>\n;(() => {\n  document.querySelector('#enable-mathjax').addEventListener('click', (e) => {\n    const script = document.createElement('script')\n    script.async = true\n    script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'\n    document.head.appendChild(script)\n    e.target.remove()\n  })\n})();\n</script>\n<p>(It would be nice to run MathJax during the website's build process,\nbut I'm too lazy to do that now.\nI just want to publish this post.)</p>\n<h2 id=\"annuity\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#annuity\">Annuity</a></h2>\n<p>Wikipedia has a clear <a href=\"https://en.wikipedia.org/wiki/Amortization_calculator#The_formula\" class=\"link link-external\">annuity formula</a>:</p>\n<p>$$\nA =\n\\frac{P \\times i}\n{(1 - (1 + i)^{-n})}\n$$</p>\n<p>Where:</p>\n<ul>\n<li>\\(A\\) = monthly payment amount (= monthly principal + monthly interest)</li>\n<li>\\(P\\) = total loan amount</li>\n<li>\\(i\\) = monthly interest rate (percentage)</li>\n<li>\\(n\\) = total number of payments (= number of months)</li>\n</ul>\n<p>On that same Wikipedia page,\nthere's a formula for calculating the remaining loan amount after \\(t\\) months\n(function \\(p(t)\\)):</p>\n<p>$$\np(t) = Pr^t - A \\frac{r^t - 1}{r - 1}\n$$</p>\n<p>Where:</p>\n<p>$$r = 1 + i$$</p>\n<p>It's clearer to use \\(1 + i\\) in place of \\(r\\):</p>\n<p>$$\np(t) = P(1 + i)^t - A \\frac{(1 + i)^t - 1}{(1 + i) - 1}\n$$</p>\n<p>Because now we can simplify \\((1 + i) - 1\\) to just \\(i\\):</p>\n<p>$$\np(t) = P(1 + i)^t - A \\frac{(1 + i)^t - 1}{i}\n$$</p>\n<p>In JavaScript,\nwith an opening fee and extra monthly fees (not included in the formulas above):</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">calculateAnnuity</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span>\n  loanAmount<span class=\"token punctuation\">,</span>\n  monthlyFee<span class=\"token punctuation\">,</span>\n  monthlyInterestPercentage<span class=\"token punctuation\">,</span>\n  months<span class=\"token punctuation\">,</span>\n  openingFee<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> monthlyAnnuity <span class=\"token operator\">=</span>\n    <span class=\"token punctuation\">(</span>loanAmount <span class=\"token operator\">*</span> monthlyInterestPercentage<span class=\"token punctuation\">)</span> <span class=\"token operator\">/</span>\n    <span class=\"token punctuation\">(</span><span class=\"token number\">1</span> <span class=\"token operator\">-</span> <span class=\"token punctuation\">(</span><span class=\"token number\">1</span> <span class=\"token operator\">+</span> monthlyInterestPercentage<span class=\"token punctuation\">)</span> <span class=\"token operator\">**</span> <span class=\"token operator\">-</span>months<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">const</span> monthlyPayment <span class=\"token operator\">=</span> monthlyAnnuity <span class=\"token operator\">+</span> monthlyFee\n  <span class=\"token keyword\">const</span> totalPayable <span class=\"token operator\">=</span> openingFee <span class=\"token operator\">+</span> monthlyPayment <span class=\"token operator\">*</span> months\n  <span class=\"token keyword\">const</span> totalFees <span class=\"token operator\">=</span> totalPayable <span class=\"token operator\">-</span> loanAmount\n\n  <span class=\"token keyword\">const</span> loanAmountLeftAfterOneYear <span class=\"token operator\">=</span>\n    loanAmount <span class=\"token operator\">*</span> <span class=\"token punctuation\">(</span><span class=\"token number\">1</span> <span class=\"token operator\">+</span> monthlyInterestPercentage<span class=\"token punctuation\">)</span> <span class=\"token operator\">**</span> <span class=\"token number\">12</span> <span class=\"token operator\">-</span>\n    monthlyAnnuity <span class=\"token operator\">*</span>\n      <span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span> <span class=\"token operator\">+</span> monthlyInterestPercentage<span class=\"token punctuation\">)</span> <span class=\"token operator\">**</span> <span class=\"token number\">12</span> <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">/</span> monthlyInterestPercentage<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    monthlyPayment<span class=\"token punctuation\">,</span>\n    totalPayable<span class=\"token punctuation\">,</span>\n    totalFees<span class=\"token punctuation\">,</span>\n    loanAmountLeftAfterOneYear<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h2 id=\"fixed-principal\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#fixed-principal\">Fixed principal</a></h2>\n<p>The formulas are much simpler\nbecause the monthly principal amount is fixed:</p>\n<ul>\n<li>monthly payment amount = monthly principal + interest for the remaining loan amount\n<ul>\n<li>monthly principal = loan amount / number of months</li>\n<li>interest for the remaining loan amount = remaining loan amount × monthly interest rate</li>\n</ul>\n</li>\n<li>loan left after \\(t\\) months = loan amount - monthly principal × \\(t\\)</li>\n</ul>\n<p>In JavaScript:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">calculateFixedPrincipal</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span>\n  loanAmount<span class=\"token punctuation\">,</span>\n  monthlyFee<span class=\"token punctuation\">,</span>\n  monthlyInterestPercentage<span class=\"token punctuation\">,</span>\n  months<span class=\"token punctuation\">,</span>\n  openingFee<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> monthlyPrincipal <span class=\"token operator\">=</span> loanAmount <span class=\"token operator\">/</span> months\n\n  <span class=\"token keyword\">const</span> monthlyPayments <span class=\"token operator\">=</span> Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> months <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">_<span class=\"token punctuation\">,</span> monthIndex</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> interest <span class=\"token operator\">=</span>\n      <span class=\"token punctuation\">(</span>loanAmount <span class=\"token operator\">-</span> monthlyPrincipal <span class=\"token operator\">*</span> monthIndex<span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> monthlyInterestPercentage\n    <span class=\"token keyword\">return</span> monthlyPrincipal <span class=\"token operator\">+</span> interest <span class=\"token operator\">+</span> monthlyFee\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> firstMonthPayment <span class=\"token operator\">=</span> monthlyPayments<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span>\n  <span class=\"token keyword\">const</span> lastMonthPayment <span class=\"token operator\">=</span> monthlyPayments<span class=\"token punctuation\">.</span><span class=\"token function\">at</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">sum</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">array</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> array<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> a <span class=\"token operator\">+</span> b<span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> totalPayable <span class=\"token operator\">=</span> openingFee <span class=\"token operator\">+</span> <span class=\"token function\">sum</span><span class=\"token punctuation\">(</span>monthlyPayments<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">const</span> totalFees <span class=\"token operator\">=</span> totalPayable <span class=\"token operator\">-</span> loanAmount\n  <span class=\"token keyword\">const</span> loanAmountLeftAfterOneYear <span class=\"token operator\">=</span> loanAmount <span class=\"token operator\">-</span> monthlyPrincipal <span class=\"token operator\">*</span> <span class=\"token number\">12</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    firstMonthPayment<span class=\"token punctuation\">,</span>\n    lastMonthPayment<span class=\"token punctuation\">,</span>\n    totalPayable<span class=\"token punctuation\">,</span>\n    totalFees<span class=\"token punctuation\">,</span>\n    loanAmountLeftAfterOneYear<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h2 id=\"example\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example\">Example</a></h2>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> referenceRate <span class=\"token operator\">=</span> <span class=\"token number\">1.482</span> <span class=\"token comment\">// E.g. Euribor 12</span>\n<span class=\"token keyword\">const</span> margin <span class=\"token operator\">=</span> <span class=\"token number\">0.55</span>\n\n<span class=\"token keyword\">const</span> specs <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">loanAmount</span><span class=\"token operator\">:</span> <span class=\"token number\">135_000</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">monthlyFee</span><span class=\"token operator\">:</span> <span class=\"token number\">2.5</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">monthlyInterestPercentage</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>referenceRate <span class=\"token operator\">+</span> margin<span class=\"token punctuation\">)</span> <span class=\"token operator\">/</span> <span class=\"token number\">12</span> <span class=\"token operator\">/</span> <span class=\"token number\">100</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">months</span><span class=\"token operator\">:</span> <span class=\"token number\">25</span> <span class=\"token operator\">*</span> <span class=\"token number\">12</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">openingFee</span><span class=\"token operator\">:</span> <span class=\"token number\">150</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token function\">calculateAnnuity</span><span class=\"token punctuation\">(</span>specs<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> {</span>\n<span class=\"token comment\">//   monthlyPayment:                 576.81,</span>\n<span class=\"token comment\">//   totalPayable:               173_192.65,</span>\n<span class=\"token comment\">//   totalFees:                   38_192.65,</span>\n<span class=\"token comment\">//   loanAmountLeftAfterOneYear: 130_812.64,</span>\n<span class=\"token comment\">// }</span>\n\n<span class=\"token function\">calculateFixedPrincipal</span><span class=\"token punctuation\">(</span>specs<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> {</span>\n<span class=\"token comment\">//   firstMonthPayment:              681.10,</span>\n<span class=\"token comment\">//   lastMonthPayment:               453.26,</span>\n<span class=\"token comment\">//   totalPayable:               170_304.30,</span>\n<span class=\"token comment\">//   totalFees:                   35_304.30,</span>\n<span class=\"token comment\">//   loanAmountLeftAfterOneYear: 129_600.00,</span>\n<span class=\"token comment\">// }</span>\n</span></code></pre>\n<p>As you can see,\nfixed principal is more expensive in the beginning\nbut cheaper in the long term (less total fees).\nAlso, at some point the monthly payments become less than in annuity.</p>\n<h3 id=\"thoughts\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#thoughts\">Thoughts</a></h3>\n<p>At first I though that fixed principal would be better\nbecause it incurs less total fees,\nbut on the other hand:</p>\n<ul>\n<li>With my specs (see above) the difference in total fees is only 2,888.35 € <em>over 25 years</em> –\nthat's only 115.53 € per year,\nor 9.63 € per month.\nNot very much.</li>\n<li>Less than ten bucks per month is not a high price to pay\nto get consistent monthly payments\nand more affordable payments in the beginning.\nEspecially the second point (more affordable payments in the beginning)\nfeels more important than slightly smaller total fees over a long period of time\n(25 years! I'm hardly that old myself).</li>\n</ul>\n<p>Here's why I wanted to calculate how much of the loan amount would be left after one year:</p>\n<ul>\n<li>Interest rates have recently been increasing rapidly.</li>\n<li>My reference rate is tied to Euribor 12,\ni.e. the rate is updated every 12 months.</li>\n<li>I wanted to check how much the monthly payments would be after one year\nif the interest rate had raised to 6% (stress test) –\nwould it matter whether I had been paying the first year with annuity or fixed principal?</li>\n<li>The difference in the remaining loan amount after one year is only 1,212.64 €,\nso the effect on the stress test scenario is negligible.\nSo no, it doesn't really matter which payment type I use in the first year.\n<ul>\n<li>There <em>is</em> something I could do to make my life easier in a stress test scenario:\nchoosing annuity would make the first monthly payments significantly cheaper\n(around 250 € in my case).</li>\n</ul>\n</li>\n</ul>\n<p>To help me choose:</p>\n<ul>\n<li>I should choose fixed principal if I want to:\n<ul>\n<li>Save less than 3k € in 25 years.\nNot a compelling reason.</li>\n<li>Pay less each month after some point.\nNot very interesting because that would be in the distant future.</li>\n</ul>\n</li>\n<li>I should choose annuity if I want to:\n<ul>\n<li>Pay about 100 € less each month (in the beginning).\nThis sounds good; I could instead invest the money (either in an index fund or in cheese! I like cheese).</li>\n<li>Pay less in a stress test scenario (6% interest rate).\nThis would increase my peace of mind.</li>\n</ul>\n</li>\n</ul>\n<p>I think I might go with annuity.</p>\n<h2 id=\"demo\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#demo\">Demo</a></h2>\n<p>I started building a calculator for comparing mortgages but lost interest quite quickly,\nso here's a half-baked demo:</p>\n<p><a href=\"https://flems.io/#0=N4IgtglgJlA2CmIBcBmADAOgGwA4A0IAzvAgMYAu8UyIpA9gHYBmEA5hgFaEgGGkBOdWLGQBtNHjQBdAiwTcko0AwCGYRElqMW7Ljy0NKhmgHoTAAgCydfuVYrW8c6RWxSAV1gryN84ApyAB0GM3MAC3JyAAdCJDMwckIAawYGDBYTACNYOlYTMBs7B3gAWhc3T28bEyCg+gZCcmd3fn54BlIAT3MAXnMAcgBRAFUAJT7axgbzbLKnXr6WcYYgkIBBHum6FQZzNTp3QxWLACENjvgVfkIj8wBhDdameFb2p35vJwAKeAx2cwHmhAMr4AIwAJgAlDcACIbMCXVgQZbBCwDDZ0SJtJGscxPeA3ABicMY5FCsC6eJuAHENqp1BN6o0MttEoQNqIguZzCFzOsufzTvz7vyubCRWiuUT+VTOeZRCCUABWAD6aDVeHMYMVGpBGAALDgwRrMIrtfyJJqMGa+scWby+jJZfKlar1ZqzbqDUbzJg9RawW6wVaNTa7ccHXgneCAJzKrCBvUarAYNUWzBurn+4P9ADK5FahDZlAaEaCUn0xDI5AgkxoIJwSBBIAAvnhlGoNOAVEjONwCHUjOQaHUpmUPF5KKsUu4IOQur1PsBZdltqt8gdyJGduZ8oYyR0CfB4FuubvSeSAJKGZ7wBoABWepDa5CKJ53JNChDfGKxDFYh+PIJmwhHoAD5zCXbcR0aM99ynBgZznHpZS5T4VwYNd9kMcwACp3z3S9rwLcgH34J9DCKEDqm3VCQXMYpzE+OiAGp8PPDor0oYjSPIl9HBAnC8OKWDCChZEuWgtj9zvFQOnUbDelg8l4MQrpWKUg8j1lSSfBfWAZI6FQsjmcwfwYbEAPMdSP3JAz5MaPCRO0yZGl01wALZXo3P02SjIQejNlXddDnE5wXMCjDgvIAAZeAmHIVZ4ueAB5Bh4AATQufhkJoiLMI3XDGJYqTCK428SMfZ9KNwvDwXolCSo6FTZy6HCGtQz4mKsxrOJve9Koo-iavMOqGJBKieqI8qeKq-iam3VpyGaHZIJFDS7OfN8uW8gy-MA3LvI8ra8qi2L4sSrjUoyrK32bID5skscKkoAkIAADyoO9+CRUgIEiVwNkXZctkirDN1lDSALfDTeu4ga+P208Py-WUzIso8t2AsCIOcxlGq+n6-oB3p0Py7CLCc0LJPW2T7M83kWlktJBDARdpjaVhSSQKS2VbRjlQ1WCrygeA3pA7pwNW-lJKRMqpm6drGNJqKAvW772iJ2BCqFhgRbF7WbI4qb+rI2b8Vyxblvx9Xfv+rXWNlvrGmsgjNPNrlgNxqYWCuchrD3DaFPx2nn0IcQpC9xovAaf3SUDxpFMN+PCAwbxPmKcb5ok8LCHcMBAcud4OnF8DC6Z1ooHcJ9OpUDUMhL3ZuoyY0xOzvGdt84z0Uxcy-0s1jc9Zmm5NDsTI-MQ6j3pjvDK7hjlbB8eF43M6EqS-grsyy4NmX7CGLVwm7cK8Es-MS3+BWhqfZjj94+O6O-dvkPDGOme9tfug9KOhrd5iuK18umlLe-Bbr3WRJJJgNh4QJxxtuXc8AOjc0+AhMAGRngamAOYKAbBZz0zBOYZsGxgBYwlg1FBaD+AYB8NFOgsw8zq1YGhWhrhjywJFBJZoLxOjHVPCoN6EAwB5wJO8CgNYGDQhwQkbm2DEQJB4TuJEAihEiOrIwCRsiYhYMkSjXKXIGgdAQNzPoHgWhtE6H0Y6wE3yYlNoYJB5DnglwasgvOFDuQjTVBCKhdAaF0PzNiJhswMGK3hPwwRYBhEqFEWo7R3MUDyMgOZcJkToniNieYeJit9GGP6DY3iFiGpWLAUEKAtC87PgwMCKAHQMBIjSvwAAEgAFUsNFDYJg8LNNabhCwAADWUAAeF8XcnzCEIP9X6f4FYgDQAEEAoEGpDNCBcKACzdHmCGfwNZ7D+RLLCrAcZ2xpk4Dmc4aOhBpm8WeOYdwkQ8kqGIOYYE-ARb8GKPwOZoEcyYlIIQAZJhSTbJ2Rs0k+zDkMGmXqU5pBzmXOfNc259zHnPNee8z5zU5z-MBYrLkez6AHP+hCuZipoWwrmVc7KiLHwPPxPM16H0oDmEiDbTWWLQhApFFirZitNkcvYXswQAB3cF0ywSkoeRcuZCB4qfIAHIdjZXyzloKhUirmWK+ZNDti7CioqnFILQhnzoMKwlorPnAL+QC9l+q8VCDVSAFA4rCxwqIp82G5U9XrNxSq419qNWgWSj3bEuIjyeuBQao1JqjnqqdZKkAKLnhovmbHfcIb4CeptT6qNRKQD+tIlJMNwKBW+tNTG+ZTTP6uELTs4t2azXlsrVrPElrsVeojaq0tubY3TITW8j5mqQbs3irsdeI0ABkDAMjjIANznEuBmttta-WfNer7AtVqlW7KzcuzVDyYIfmrfy7dnb-UVr0oe5VhqO3Rq7Q2vSaaW3WsXcem9-qtU7GlY0FQo6QQTqnZEWdWUL0bIBdy9ZvLM3spGHFG8rwz4fGA969llgERIkQwa0CZ6q0bp5aBvlbKVn4fIFUjom6AAkwBmQMFZPqjA8JIifH1ahDkbaRS-w2BIJj-I51XA2CCeROzHiwafCMD4An2Hwn4IiBg4mRRoz7keDjsn+SQ0U70TjrH+R0hMn0ApmmpANyluGtuUwYbGwqrYhGOVjPsM+EJrh8BROUG6pJ6TE06oWBBGqLjJn917npjxtktUxWhRs5JcZ8BflEJ8-yX+ym1qGyhjFpGrt3Um14q+ZLPN4v8nk-+DGMW7qad87sacLUNhPQnPADFHRPgRd+a3GzYU8YsAZQTDWR9eiVY+PSz6LK7Z1Z+aJU+Nnz47A6eYLpbScJ9KyxB4rR7DUwoldMz9nyKPaebOh8NQzVkUcgfwaBdHGAIKYUFMGGCtEaO5mgAhEItsAtWVl71e3gCBYe+QJ7C3lWvYO0dvJz47MwYc05+A92sVfaa0e37UDvAYAB4YT4rmkTg8e5umzu3QL7dh+QeH8MgdPBBx8FzqGGCo8++j4zmPseHbh-A2reWoYQSu7gm7d2PuQ6h1uxly3nVzN7UmrHwA-t05O7V1TYOOdrOeyCmHtPcf08+NsBCLVjuu3juTznXPZdC5FwrsXSuytzm8XpXaxlNeU52xTmnR3FfK9Uib9yU8Lcy922clb-ObCov7Zb4zNvRdpVq-b1Xv9V4XRSkArK93Xdo5G1z6nwucdq9O61vrh9XBpAgL7FNGupeu+t4n+Xyfaup6gO122GeH45+fuQF333ue66T4r0v5fNaO58rPBAdftcvcb0X5v7008dYz5PW83ee+Y5l1yf3+vA+fBb-1yvIMyZ-3OuvTeUeis95AxTubeGYv9M01v4FjWdmcDoEiT4uno+5SxSRoFWK9prMPy2NsIBtM0HMk+DAvy+wGEHDQEgIIJ-GwvRMUL2tzKwEXNOrKJAoYMUEwGoBAOSNzLuHQIck+DAduHAeQMUIQBAAAF7wDczRhoAACkWBRWQQwy-kUsEBI0kQb05ghAQg0A5gAAbpcOnOAV7o4lgVyALp+sUIKtAFzJqIwfwU8rwW8j4JEMIaIaENzGCBIcUnuKAcyKQIkFAVhFAKUEIDYNzAAMRHjwCSHsHPDVhlDFCuBsAMDczAgRB0BgCUHzSkgaifbqHSE8GOFgDcwgiMHMGsGMqcH8DcG9oQiSEC7fSsDhB+EBEsGwBsEhFhHSERGyj-QwDYg3ZWitDOGyiUBvS4E2GsB2FnxsDhAuHIieCgGJFpTFDLLlHkB+EYBaiSHI6lFoCSEZHYJ-jFCfp+G5GVFBA-7wrZSYIFFFGJElHcwUrToELDGfr8jjGiyTG2HcyfpzEezzSVJeFjFSEvKJrRHhDyFQBiHKFvRzFUGpBUpkQ0rM4THFD5jbCEB-bcw3EuDECXEtgyDv4dg0DoTOCuDjiVD8AthAA\" class=\"link link-external\">Mortgage calculator on flems.io</a></p>\n","summary":"Instead of spending 5 minutes to look for and use an existing mortgage calculator,\nI wasted time figuring out how to do the calculations from scratch.","date_published":"2022-09-13T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/js-read-only-array-sort/","url":"https://mtsknn.fi/blog/js-read-only-array-sort/","title":"Sorting a read-only array is OK if it has only 1 or 0 items","content_html":"<p>Huh.\nBut if the array has 2+ items,\nattempting to sort it will cause a TypeError.</p>\n<h2 id=\"example-1\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-1\">Example 1</a></h2>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">Object<span class=\"token punctuation\">.</span><span class=\"token function\">freeze</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK</span>\nObject<span class=\"token punctuation\">.</span><span class=\"token function\">freeze</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK</span>\nObject<span class=\"token punctuation\">.</span><span class=\"token function\">freeze</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'b'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// TypeError</span>\n</span></code></pre>\n<p>Errors in different browsers:</p>\n<ul>\n<li>Chrome / Edge: &quot;TypeError: Cannot assign to read only property '0' of object '[object Array]'&quot;</li>\n<li>Firefox: &quot;TypeError: 0 is read-only&quot;</li>\n<li>Safari: &quot;TypeError: Attempted to assign to readonly property.&quot;</li>\n</ul>\n<h2 id=\"example-2\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-2\">Example 2</a></h2>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> arr <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\narr<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token string\">'b'</span>\nObject<span class=\"token punctuation\">.</span><span class=\"token function\">freeze</span><span class=\"token punctuation\">(</span>arr<span class=\"token punctuation\">)</span>\narr<span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// TypeError</span>\n</span></code></pre>\n<p>The errors are slightly different:</p>\n<ul>\n<li>Chrome / Edge: &quot;TypeError: Cannot add property 0, object is not extensible&quot;</li>\n<li>Firefox: &quot;TypeError: can't define property 0: Array is not extensible&quot;</li>\n<li>Safari: same as in Example 1</li>\n</ul>\n<h2 id=\"pondering\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#pondering\">Pondering</a></h2>\n<p>I use mainly Firefox.\nIts error message &quot;0 is read-only&quot; is not very enlightening,\nso when investigating a related bug,\nI didn't grasp at first that the problem was about a read-only array.\nMeh.</p>\n<p>If an array is empty or has only one item,\nsorting the array won't modify the array\nbecause there are not enough items to move around.\nSo no error even if the array is read-only.\nMakes sense but was surprising (hence this blog post).</p>\n<p>On the other hand:\nif a read-only array has 2+ items and is already sorted,\ntrying to sort it causes a TypeError,\neven though the array is already sorted!\nMakes sense but is also mildly confusing –\nwhy is the array being modified if it's already sorted?</p>\n<p>If a read-only array has only <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#sparse_arrays\" class=\"link link-external\">empty slots</a>,\nsorting it is OK,\nwhich is weird:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> arr <span class=\"token operator\">=</span> Object<span class=\"token punctuation\">.</span><span class=\"token function\">freeze</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\narr<span class=\"token punctuation\">.</span>length <span class=\"token comment\">//=> 3</span>\narr<span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK but weird</span>\n</span></code></pre>\n<h2 id=\"fix-1\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#fix-1\">Fix 1</a></h2>\n<p><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort\" class=\"link link-external\">The <code>sort() method</code></a>\nsorts an array in-place,\ni.e. sorting an array modifies it.</p>\n<p>You might want to\nfirst <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice\" class=\"link link-external\">copy the array with <code>slice()</code></a>\nand then sort the copied array:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> arr <span class=\"token operator\">=</span> Object<span class=\"token punctuation\">.</span><span class=\"token function\">freeze</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'b'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\narr<span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// TypeError</span>\narr<span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK</span>\n</span></code></pre>\n<h2 id=\"fix-2\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#fix-2\">Fix 2</a></h2>\n<p>TypeScript would have spotted the error for me right away:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> arr <span class=\"token operator\">=</span> Object<span class=\"token punctuation\">.</span><span class=\"token function\">freeze</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'b'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\narr<span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//  ^? Property 'sort' does not exist on type 'readonly string[]'.</span>\n</span></code></pre>\n<p>On the other hand,\nthe TypeScript compiler doesn't seem to know that sorting a read-only array is OK\nif the array has only 1 or 0 items:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> arr <span class=\"token operator\">=</span> Object<span class=\"token punctuation\">.</span><span class=\"token function\">freeze</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\narr<span class=\"token punctuation\">.</span><span class=\"token function\">sort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//  ^? Property 'sort' does not exist on type 'readonly string[]'.</span>\n\n<span class=\"token comment\">// 👆 Compile-time error but no runtime error, so 🤷‍♂️</span>\n</span></code></pre>\n<p>Ha! 😄</p>\n<p>On a serious note:\nI think it's better that the TypeScript compiler always disallows sorting read-only arrays.</p>\n","summary":"Huh.\nBut if the array has 2+ items,\nattempting to sort it will cause a TypeError.","date_published":"2022-09-01T12:00:00+03:00","tags":["JavaScript","TypeScript"]},{"id":"https://mtsknn.fi/blog/tongue-position/","url":"https://mtsknn.fi/blog/tongue-position/","title":"Correct tongue position in 3 steps","content_html":"<p>Say &quot;n&quot; and cluck and relax. Easy!\nCorrect tongue position makes nasal breathing easier.</p>\n<h2 id=\"step-1-find-the-right-spot-for-the-tip-of-the-tongue\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-1-find-the-right-spot-for-the-tip-of-the-tongue\">Step 1: Find the right spot for the tip of the tongue</a></h2>\n<p>The right spot is in the roof of the mouth,\nbehind the upper front teeth.</p>\n<p>Saying the letter &quot;n&quot; aloud will guide the tip of the tongue to the right spot.</p>\n<h2 id=\"step-2-place-the-rest-of-the-tongue-against-the-roof-of-the-mouth\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-2-place-the-rest-of-the-tongue-against-the-roof-of-the-mouth\">Step 2: Place the rest of the tongue against the roof of the mouth</a></h2>\n<p>Placing the whole tongue to the roof of the mouth\nwill pull down the roof of the mouth (i.e. the hard palate).\nThe roof of the mouth is also the floor of the nose,\nso this helps increase the nasal space,\nmaking breathing through the nose easier.</p>\n<p>One way to guide the whole tongue to the roof of the mouth\nis to cluck with your tongue.\n(Check the video link below for an example.)</p>\n<h2 id=\"step-3-relax\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-3-relax\">Step 3: Relax</a></h2>\n<p>Keep your lips together <em>gently</em>.\nRelax the whole head as well:</p>\n<ul>\n<li>Relax the tongue</li>\n<li>Relax the lips</li>\n<li>Relax the jaws</li>\n<li>Relax the eyes</li>\n<li>Relax the face</li>\n</ul>\n<h3 id=\"bonus-relax-the-diaphragm\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#bonus-relax-the-diaphragm\">Bonus: Relax the diaphragm</a></h3>\n<p>Keep the diaphragm relaxed as well\nand practice <a href=\"https://en.wikipedia.org/wiki/Diaphragmatic_breathing\" class=\"link link-external\">diaphragmatic breathing</a>\nat all times.</p>\n<h2 id=\"video-guide\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#video-guide\">Video guide</a></h2>\n<p>The three steps are from\n<a href=\"https://www.youtube.com/watch?v=MBPBthM1lcw\" class=\"link link-external\">a YouTube video called <em>STOP Mouth Breathing – Try This</em></a>\nthat I accidentally found when looking for something else.\n(I slightly expanded the 3rd step above and added the bonus step.)</p>\n<p>The video starts by explaining some benefits of nasal breathing.\n<a href=\"https://www.youtube.com/watch?v=MBPBthM1lcw&amp;t=102s\" class=\"link link-external\">The 3-step instructions start at 1:42 of the video.</a></p>\n<h2 id=\"why\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why\">Why?</a></h2>\n<p>Nasal breathing (i.e. breathing in and out through the nose) at all times\nis essential for your health.\n&quot;At all times&quot; includes breathing while sleeping and during exercise!</p>\n<p>If you are into running,\ntry running with the mouth closed.\nIt will be hard at first,\nbut try it for a month.\nI'm sure you'll like it so much more\nthat you'll never want to run with an open mouth anymore.</p>\n<h2 id=\"the-2nd-step-is-important\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-2nd-step-is-important\">The 2nd step is important</a></h2>\n<p>I'm calling it out because\nI have been practicing nasal breathing for several years,\nbut I didn't know that the whole tongue should be against the roof of the mouth.\nWhoops. 🙉\nI just recently learned it from the video.</p>\n<p>Now nasal breathing without the 2nd step –\ni.e. with the tip of the tongue at the roof of the mouth\nbut the rest of the tongue hanging at the floor of the mouth –\nfeels somewhat weird.\nProbably because without the step the nasal space is reduced.</p>\n<p>Don't make the mistake that I was doing for several years. 🤙</p>\n","summary":"Say \"n\" and cluck and relax. Easy!\nCorrect tongue position makes nasal breathing easier.","date_published":"2022-08-16T12:00:00+03:00","tags":["Breathing","Health","Running"]},{"id":"https://mtsknn.fi/blog/running-vs-jogging/","url":"https://mtsknn.fi/blog/running-vs-jogging/","title":"Running vs jogging","content_html":"<p>Jogging is one type of running.\nWhen I run, I mostly jog,\nbut I still think I'm a runner, not a jogger.\nHuh?</p>\n<p>Here's <a href=\"https://www.dictionary.com/browse/jog\" class=\"link link-external\">one definition for jogging on Dictionary.com</a>:</p>\n<blockquote>\n<p>to run at a leisurely, slow pace, especially as an outdoor exercise</p>\n</blockquote>\n<p>So, jogging is slow running.</p>\n<p>Jogging is running,\nbut not all running is jogging.\nFor example,\nsprinting is also running\n(but sprinting is not jogging).</p>\n<p>What is &quot;slow running&quot; is personal.\nIf you are running and feel that you could easily run faster,\nmaybe your current pace is a jogging pace (i.e. a slow pace).</p>\n<p>It doesn't matter much which word you use.\nThough sometimes the distinction can be useful\nas <a href=\"https://old.reddit.com/r/running/comments/ns8r3d/do_you_feel_theres_a_difference_between_jogging/h0kzgf5/\" class=\"link link-external\">someone writes on Reddit</a>:</p>\n<blockquote>\n<p>I feel like they're interchangeable for the most part.</p>\n<p>But like,\nif I'm talking to a non-runner about my workouts for some reason,\nI might say something like\n&quot;I'll run fast for a mile,\nthen jog for a half mile,\nand repeat that six times&quot;\njust because I feel like they'll get that &quot;jog&quot; means &quot;run slower&quot; in that instance.</p>\n</blockquote>\n<p>&quot;Running&quot; also sounds cooler than &quot;jogging,&quot;\nso I consider myself a runner,\neven though I almost always aim for easy runs.</p>\n","summary":"Jogging is one type of running.\nWhen I run, I mostly jog,\nbut I still think I'm a runner, not a jogger.\nHuh?","date_published":"2022-08-15T12:00:00+03:00","tags":["English","Running"]},{"id":"https://mtsknn.fi/blog/pocket-phone/","url":"https://mtsknn.fi/blog/pocket-phone/","title":"Stop keeping your phone in your pocket","content_html":"<p>A trouser pocket is way too easily reachable.\nI often find myself reaching for my phone automatically\neven if I don't have anything to do with the phone.</p>\n<h2 id=\"discipline\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#discipline\">Discipline</a></h2>\n<p>Maybe you can reduce your phone usage with discipline,\nbut then it doesn't make sense to keep your phone one hand gesture away.\nIt won't be just about discipline anymore;\nit will also be about addiction and automatic habits.</p>\n<h2 id=\"addiction\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#addiction\">Addiction</a></h2>\n<p>Addiction is easily more powerful than discipline.\nEspecially if you need to be disciplined all the time,\nlike if you have your phone always easily reachable (in your pocket).\nTrying to be disciplined all the time wastes mental energy.</p>\n<p>It doesn't make sense for a drug addict to keep drugs in their pocket.</p>\n<p>If you are addicted to your phone –\nand I think most people are –\nyou are a junkie and your phone is your drug.</p>\n<h2 id=\"automatic-habits\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#automatic-habits\">Automatic habits</a></h2>\n<p>Taking a phone from your pocket is just one step:\nput your hand in your pocket and grab the phone.\nIt's easy to do that automatically,\nwithout even thinking about that.</p>\n<p>Trying to avoid automatic habits wastes mental energy too.\nInstead, make the habit impossible –\nkeep your phone away from your pocket.</p>\n<p>If you are at home,\nkeep your phone out of sight,\nlike in a closet behind doors\n(but not in your bedroom!).</p>\n<p>If you are elsewhere,\nkeep your phone in a back bag.\nTaking your phone now requires more than one step:\nfirst you have to take the bag from your back,\nthan open a zipper,\nand <em>then</em> can you grab the phone.\nDoing these steps probably isn't very automatic.</p>\n<p>A fanny pack would be cool too,\nthough I wonder if taking a phone from one would quickly become too easy and automatic.</p>\n","summary":"A trouser pocket is way too easily reachable.\nI often find myself reaching for my phone automatically\neven if I don't have anything to do with the phone.","date_published":"2022-08-04T12:00:00+03:00","tags":["NoSurf"]},{"id":"https://mtsknn.fi/blog/toggleable-react-state/","url":"https://mtsknn.fi/blog/toggleable-react-state/","title":"Toggleable React state with a 1-line `useReducer`","content_html":"<p>Say goodbye to <code>useState</code>\nand a silly toggler function calling the state setter.\nBe cool and use a tiny reducer instead.</p>\n<h2 id=\"example-enabled-state\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-enabled-state\">Example: &quot;enabled&quot; state</a></h2>\n<h3 id=\"typical-solution-with-usestate\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#typical-solution-with-usestate\">Typical solution with <code>useState</code></a></h3>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">Component</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>enabled<span class=\"token punctuation\">,</span> setEnabled<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">toggleEnabled</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setEnabled</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">previous</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token operator\">!</span>previous<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>toggleEnabled<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Toggle</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>2 lines – so long and verbose!</p>\n<h3 id=\"cool-solution-with-usereducer\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#cool-solution-with-usereducer\">Cool solution with <code>useReducer</code></a></h3>\n<p><a href=\"https://reactjs.org/docs/hooks-reference.html#usereducer\" class=\"link link-external\">React's <code>useReducer</code> hook</a>\ntakes a reducer function and an initial value:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">Component</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>enabled<span class=\"token punctuation\">,</span> toggleEnabled<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useReducer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">previous</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token operator\">!</span>previous<span class=\"token punctuation\">,</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>toggleEnabled<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Toggle</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Only 1 line.\nSo cool! 😎</p>\n<p>This is a bit cryptic at first,\nand maybe I wouldn't use it in any &quot;real&quot; code,\nbut I found this intriguing and wanted to share it.</p>\n<p>I picked this from\n<a href=\"https://tkdodo.eu/blog/things-to-know-about-use-state\" class=\"link link-external\">TkDodo's blog post <em>Things to know about useState</em></a>\n(from the &quot;Bonus 2&quot; section).</p>\n<h2 id=\"another-example-toggling-a-numeric-value\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#another-example-toggling-a-numeric-value\">Another example: toggling a numeric value</a></h2>\n<p>Toggling between 250 and 750:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">Component</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>duration<span class=\"token punctuation\">,</span> toggleDuration<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useReducer</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">(</span><span class=\"token parameter\">previous</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>previous <span class=\"token operator\">+</span> <span class=\"token number\">500</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">%</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">,</span>\n    <span class=\"token number\">250</span>\n  <span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>toggleDuration<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Toggle</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Whoops, 4 lines. 🥲\nBut I invented this myself! 😄</p>\n","summary":"Say goodbye to `useState`\nand a silly toggler function calling the state setter.\nBe cool and use a tiny reducer instead.","date_published":"2022-06-05T12:00:00+03:00","tags":["JavaScript","React"]},{"id":"https://mtsknn.fi/blog/auto-animate/","url":"https://mtsknn.fi/blog/auto-animate/","title":"1 line of code to animate element additions, removals and moves","content_html":"<p>AutoAnimate is a JS library that provides simple animations with no effort.\nWorks with Mithril.js, React and any other JS app.</p>\n<h2 id=\"highlights\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#highlights\">Highlights</a></h2>\n<ul>\n<li><a href=\"https://auto-animate.formkit.com/\" class=\"link link-external\">AutoAnimate</a> is a &quot;zero-config, drop-in animation utility&quot;</li>\n<li>Improves UX very much\n<ul>\n<li>Animated element modifications are easier to spot</li>\n</ul>\n</li>\n<li>Very easy to use: just a single function (or React hook)\n<ul>\n<li>Great for business apps when you can't spend time on fancy stuff like animations\n(but great for other apps as well)</li>\n</ul>\n</li>\n<li>Very lightweight\n<ul>\n<li><a href=\"https://bundlephobia.com/package/@formkit/auto-animate@1.0.0-beta.1\" class=\"link link-external\">BundlePhopia reports AutoAnimate's minzipped size to be only 2.2 KB</a></li>\n</ul>\n</li>\n</ul>\n<h2 id=\"reactjs-example\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#reactjs-example\">React.js example</a></h2>\n<p>From <a href=\"https://auto-animate.formkit.com/\" class=\"link link-external\">AutoAnimate's website</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> useAutoAnimate <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'@formkit/auto-animate/react'</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">MyList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>animationParent<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useAutoAnimate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>ul</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>animationParent<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* 🪄 Magic animations for your list */</span><span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>ul</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>If the import statement is not counted,\nthat's only two lines of code.\nNice!</p>\n<p>(The <a href=\"#mithriljs-example\" class=\"link link-anchor\">Mithril.js example</a> below works with only one line of code.)</p>\n<h3 id=\"empty-lessulgreater-is-problematic\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#empty-lessulgreater-is-problematic\">Empty <code>&lt;ul&gt;</code> is problematic</a></h3>\n<p>If you don't want to render an empty <code>&lt;ul&gt;</code> element,\nyou need to use AutoAnimate on the <code>&lt;ul&gt;</code>'s parent element too:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> useAutoAnimate <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'@formkit/auto-animate/react'</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">MyList</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> items <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>parent<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useAutoAnimate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>list<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useAutoAnimate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>parent<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token punctuation\">{</span>items<span class=\"token punctuation\">.</span>length <span class=\"token operator\">></span> <span class=\"token number\">0</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>ul</span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>list<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* Render items */</span><span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>ul</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>But there's currently an open React-specific GitHub issue that\n<a href=\"https://github.com/formkit/auto-animate/issues/8\" class=\"link link-external\">AutoAnimate doesn't work with containers that are not initially rendered</a>.</p>\n<h3 id=\"manual-exit-animations-would-be-difficult\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#manual-exit-animations-would-be-difficult\">Manual exit animations would be difficult</a></h3>\n<p>Animating the removal of an element is tricky in React\nbecause when React removes an element from the DOM,\nthe element is removed immediately.\nThere's no time to run CSS animations.</p>\n<p>If I interpreted <a href=\"https://github.com/formkit/auto-animate\" class=\"link link-external\">AutoAnimate's source code</a> correctly,\nit animates element removal like this:</p>\n<ol>\n<li>React (or whatever you're using) removes an element from the DOM.</li>\n<li>AutoAnimate adds the element back to the DOM!</li>\n<li>AutoAnimate triggers an animation for the element.</li>\n<li>AutoAnimate removes the element from the DOM\nafter the animation is finished.</li>\n</ol>\n<p>Sounds a bit hacky and risky to modify a React-rendered DOM like this,\nbut seems to work fine.\nQuite clever actually!</p>\n<h2 id=\"mithriljs-example\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#mithriljs-example\">Mithril.js example</a></h2>\n<p><a href=\"https://mithril.js.org/\" class=\"link link-external\">Mithril.js</a> is so cool that you only need one line of code:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> autoAnimate <span class=\"token keyword\">from</span> <span class=\"token string\">'@formkit/auto-animate'</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">MyList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function-variable function\">view</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n      <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ul'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token function-variable function\">oncreate</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">vnode</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">autoAnimate</span><span class=\"token punctuation\">(</span>vnode<span class=\"token punctuation\">.</span>dom<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token comment\">// Render items</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>The <a href=\"https://mithril.js.org/lifecycle-methods.html#oncreate\" class=\"link link-external\"><code>oncreate</code> lifecycle hook in Mithril.js</a>\nis like the good old <a href=\"https://reactjs.org/docs/react-component.html#componentdidmount\" class=\"link link-external\"><code>componentDidMount</code> lifecycle method in React</a>.</p>\n<p><code>vnode.dom</code> points to the DOM element, so it's like a ref in React.</p>\n<p>In Mithril.js,\ncomponents <em>and</em> virtual DOM nodes have lifecycle hooks\nand &quot;automatic&quot; refs (no need for something like <code>createRef</code> or <code>useRef</code>).\nSuper useful.</p>\n<h3 id=\"empty-lessulgreater-is-no-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#empty-lessulgreater-is-no-problem\">Empty <code>&lt;ul&gt;</code> is no problem</a></h3>\n<p>Like with React,\nyou'll still need to use AutoAnimate on the <code>&lt;ul&gt;</code>'s parent element too\nif you don't want to render an empty <code>&lt;ul&gt;</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> autoAnimate <span class=\"token keyword\">from</span> <span class=\"token string\">'@formkit/auto-animate'</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">MyList</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function-variable function\">view</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">attrs</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> items <span class=\"token punctuation\">}</span> <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n      <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'div'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token function-variable function\">oncreate</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">vnode</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">autoAnimate</span><span class=\"token punctuation\">(</span>vnode<span class=\"token punctuation\">.</span>dom<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n        items<span class=\"token punctuation\">.</span>length <span class=\"token operator\">></span> <span class=\"token number\">0</span> <span class=\"token operator\">&amp;&amp;</span>\n          <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ul'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token function-variable function\">oncreate</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">vnode</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">autoAnimate</span><span class=\"token punctuation\">(</span>vnode<span class=\"token punctuation\">.</span>dom<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n            <span class=\"token comment\">// Render items</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>But the aforementioned React-specific issue is not a problem with Mithril.js,\nthings just work.\nDid I say that Mithril.js is cool!</p>\n<h3 id=\"manual-exit-animations-would-be-easy\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#manual-exit-animations-would-be-easy\">Manual exit animations would be... easy!</a></h3>\n<p>The <a href=\"https://mithril.js.org/lifecycle-methods.html#onbeforeremove\" class=\"link link-external\"><code>onbeforeremove</code> lifecycle hook in Mithril.js</a>\nmakes implementing exit animations easy.\nFrom the docs (code example slightly edited by me):</p>\n<blockquote>\n<p>The <code>onbeforeremove(vnode)</code> hook is called before a DOM element is detached from the document.\nIf a Promise is returned,\nMithril.js only detaches the DOM element after the promise completes.\n[...]</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> Fader <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">onbeforeremove</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">vnode</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    vnode<span class=\"token punctuation\">.</span>dom<span class=\"token punctuation\">.</span>classList<span class=\"token punctuation\">.</span><span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fade-out'</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function-variable function\">view</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'div'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Bye'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n</blockquote>\n<p>AutoAnimate still has its place though –\nit's great for adding quick and easy animations with no effort.</p>\n","summary":"AutoAnimate is a JS library that provides simple animations with no effort.\nWorks with Mithril.js, React and any other JS app.","date_published":"2022-05-28T12:00:00+03:00","tags":["JavaScript","Mithril.js","React"]},{"id":"https://mtsknn.fi/blog/button-type-button/","url":"https://mtsknn.fi/blog/button-type-button/","title":"Your Button component should default to `type=\"button\"`","content_html":"<p>A <code>&lt;button&gt;</code> element defaults to <code>type=&quot;submit&quot;</code>\nwhich can cause accidental form submissions.\nMake <code>type=&quot;button&quot;</code> the default in your Button component\nto make your life easier.</p>\n<h2 id=\"typebutton-vs-typesubmit\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#typebutton-vs-typesubmit\"><code>type=&quot;button&quot;</code> vs <code>type=&quot;submit&quot;</code></a></h2>\n<p>A <code>&lt;button&gt;</code> element without a <code>type</code> attribute\ndefaults to <code>type=&quot;submit&quot;</code>.\nClicking a submit button associated with a <code>&lt;form&gt;</code>\nsubmits the form\nunless <code>event.preventDefault()</code> is called.</p>\n<p>A <code>&lt;button&gt;</code> element with <code>type=&quot;button&quot;</code> on the other hand\ndoes nothing by default when clicked.\nCalling <code>event.preventDefault()</code> does nothing\nbecause there's no default action to prevent.</p>\n<p>Setting your Button component to default to <code>type=&quot;button&quot;</code>\nallows you to not worry about:</p>\n<ul>\n<li>accidental form submissions</li>\n<li>having to remember call <code>event.preventDefault()</code>.</li>\n</ul>\n<p>Also, using <code>&lt;Button type=&quot;submit&quot;&gt;</code> makes it explicit that it's a submit button.</p>\n<h2 id=\"example-with-react-and-typescript\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-with-react-and-typescript\">Example with React and TypeScript</a></h2>\n<pre aria-label=\"Code block\" class=\"language-tsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">Props</span> <span class=\"token operator\">=</span> React<span class=\"token punctuation\">.</span>ButtonHTMLAttributes<span class=\"token operator\">&lt;</span>HTMLButtonElement<span class=\"token operator\">></span> <span class=\"token operator\">&amp;</span> <span class=\"token punctuation\">{</span> type<span class=\"token operator\">?</span><span class=\"token operator\">:</span> <span class=\"token string\">'submit'</span> <span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">Button</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> children<span class=\"token punctuation\">,</span> type<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>props <span class=\"token punctuation\">}</span><span class=\"token operator\">:</span> Props<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token spread\"><span class=\"token punctuation\">{</span><span class=\"token operator\">...</span>props<span class=\"token punctuation\">}</span></span> <span class=\"token attr-name\">type</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>type <span class=\"token operator\">||</span> <span class=\"token string\">'button'</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n    </span><span class=\"token punctuation\">{</span>children<span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n  </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// Usage:</span>\n\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Button</span></span> <span class=\"token punctuation\">/></span></span> <span class=\"token comment\">// Defaults to `type=\"button\"`</span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Button</span></span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>submit<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n</span></code></pre>\n<p>The <code>type</code> prop's value in <code>React.ButtonHTMLAttributes&lt;HTMLButtonElement&gt;</code>\nis <code>&quot;submit&quot; | &quot;button&quot; | &quot;reset&quot; | undefined</code>.\nI'm restricting the value to <code>&quot;submit&quot; | undefined</code>,\nso that only <code>type=&quot;submit&quot;</code> is allowed.\n(Omitting the prop is also allowed.)</p>\n<ul>\n<li><code>type=&quot;button&quot;</code> is not allowed\nbecause it's the default,\nso it would be unnecessary noise.</li>\n<li><code>type=&quot;reset&quot;</code> is not allowed\nbecause reset buttons (i.e. buttons that empty the whole form) are so stupid. 🤡\nNot on my watch!</li>\n</ul>\n","summary":"A `<button>` element defaults to `type=\"submit\"`\nwhich can cause accidental form submissions.\nMake `type=\"button\"` the default in your Button component\nto make your life easier.","date_published":"2022-05-27T12:00:00+03:00","tags":["JavaScript","React","TypeScript"]},{"id":"https://mtsknn.fi/blog/illegal-invocations-in-ts/","url":"https://mtsknn.fi/blog/illegal-invocations-in-ts/","title":"Preventing \"illegal invocation\" errors in TypeScript","content_html":"<p>The TypeScript compiler could help prevent &quot;illegal invocation&quot; errors at compile time,\nat least theoretically.</p>\n<h2 id=\"illegal-invocation-errors\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#illegal-invocation-errors\">Illegal invocation errors?</a></h2>\n<p>Chromium browsers throw an &quot;illegal invocation&quot; error when\n<strong>calling a function whose <code>this</code> keyword isn't referring to the object where it originally did</strong>,\ni.e. when the &quot;context&quot; of the function is lost.</p>\n<p>Other browsers and Node.js produce different error messages.</p>\n<p>Check out my earlier blog post for more details:\n<a href=\"/blog/illegal-invocations-in-js/\" class=\"link\">&quot;Illegal invocation&quot; errors in JavaScript</a>.</p>\n<h2 id=\"typescript-is-of-no-help-by-default\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#typescript-is-of-no-help-by-default\">TypeScript is of no help by default</a></h2>\n<p>By default,\nTypeScript doesn't balk at this code,\nresulting in runtime errors when calling the functions:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> abortController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AbortController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> abort <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> abortController\n\n<span class=\"token function\">abort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> TypeError: Illegal invocation</span>\n\n<span class=\"token keyword\">const</span> $ <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span>querySelector\n<span class=\"token keyword\">const</span> $$ <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span>querySelectorAll\n\n<span class=\"token function\">$</span><span class=\"token punctuation\">(</span><span class=\"token string\">'#foo'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> TypeError: Illegal invocation</span>\n<span class=\"token function\">$$</span><span class=\"token punctuation\">(</span><span class=\"token string\">'.bar'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> TypeError: Illegal invocation</span>\n</span></code></pre>\n<h2 id=\"declaring-the-type-of-this\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#declaring-the-type-of-this\">Declaring the type of <code>this</code></a></h2>\n<p>TypeScript <em>could</em> spot the errors at compile time\nbecause you can <a href=\"https://www.typescriptlang.org/docs/handbook/2/functions.html#declaring-this-in-a-function\" class=\"link link-external\">declare the type of a function's <code>this</code> keyword in TypeScript</a>.</p>\n<p>For example,\nthe default <code>Document</code> interface specifies the <code>querySelector</code> method like this:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">interface</span> <span class=\"token class-name\">Document</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token generic-function\"><span class=\"token function\">querySelector</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">K</span> <span class=\"token keyword\">extends</span> <span class=\"token keyword\">keyof</span> HTMLElementTagNameMap<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>\n    selectors<span class=\"token operator\">:</span> <span class=\"token constant\">K</span>\n  <span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> HTMLElementTagNameMap<span class=\"token punctuation\">[</span><span class=\"token constant\">K</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span>\n  <span class=\"token generic-function\"><span class=\"token function\">querySelector</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">K</span> <span class=\"token keyword\">extends</span> <span class=\"token keyword\">keyof</span> SVGElementTagNameMap<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>\n    selectors<span class=\"token operator\">:</span> <span class=\"token constant\">K</span>\n  <span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> SVGElementTagNameMap<span class=\"token punctuation\">[</span><span class=\"token constant\">K</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span>\n  <span class=\"token generic-function\"><span class=\"token function\">querySelector</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">E</span> <span class=\"token keyword\">extends</span> Element <span class=\"token operator\">=</span> Element<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>\n    selectors<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span>\n  <span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token constant\">E</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span>\n\n  <span class=\"token comment\">// + many other methods and properties</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>(The <code>querySelector</code> method is specified three times on purpose;\nthis is called <a href=\"https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads\" class=\"link link-external\">function overloading</a>.)</p>\n<p>If the interface also specified the type of the <code>querySelector</code> method's <code>this</code> keyword,\nthe TypeScript compiler wouldn't let you call the method illegally:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">interface</span> <span class=\"token class-name\">Document</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token generic-function\"><span class=\"token function\">querySelector</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">K</span> <span class=\"token keyword\">extends</span> <span class=\"token keyword\">keyof</span> HTMLElementTagNameMap<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>\n    <span class=\"token keyword\">this</span><span class=\"token operator\">:</span> Document<span class=\"token punctuation\">,</span>\n    selectors<span class=\"token operator\">:</span> <span class=\"token constant\">K</span>\n  <span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> HTMLElementTagNameMap<span class=\"token punctuation\">[</span><span class=\"token constant\">K</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span>\n  <span class=\"token generic-function\"><span class=\"token function\">querySelector</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">K</span> <span class=\"token keyword\">extends</span> <span class=\"token keyword\">keyof</span> SVGElementTagNameMap<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>\n    <span class=\"token keyword\">this</span><span class=\"token operator\">:</span> Document<span class=\"token punctuation\">,</span>\n    selectors<span class=\"token operator\">:</span> <span class=\"token constant\">K</span>\n  <span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> SVGElementTagNameMap<span class=\"token punctuation\">[</span><span class=\"token constant\">K</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span>\n  <span class=\"token generic-function\"><span class=\"token function\">querySelector</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span><span class=\"token constant\">E</span> <span class=\"token keyword\">extends</span> Element <span class=\"token operator\">=</span> Element<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span>\n    <span class=\"token keyword\">this</span><span class=\"token operator\">:</span> Document<span class=\"token punctuation\">,</span>\n    selectors<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span>\n  <span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token constant\">E</span> <span class=\"token operator\">|</span> <span class=\"token keyword\">null</span>\n<span class=\"token punctuation\">}</span>\n\ndocument<span class=\"token punctuation\">.</span><span class=\"token function\">querySelector</span><span class=\"token punctuation\">(</span><span class=\"token string\">'.foo'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK</span>\n\n<span class=\"token keyword\">const</span> $ <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span>querySelector\n<span class=\"token function\">$</span><span class=\"token punctuation\">(</span><span class=\"token string\">'.foo'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// Compile-time error</span>\n\n<span class=\"token comment\">// This method hasn't been patched:</span>\n<span class=\"token keyword\">const</span> $$ <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span>querySelectorAll\n<span class=\"token function\">$$</span><span class=\"token punctuation\">(</span><span class=\"token string\">'.foo'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK at compile time but results in a runtime error</span>\n</span></code></pre>\n<p>The compile-time error would be something like this:</p>\n<blockquote>\n<p>The 'this' context of type 'void' is not assignable to method's 'this' of type 'Document'.</p>\n</blockquote>\n<p><a href=\"https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgCIHsEFcC2FzIDeAUMsgI5bQCeAyhADYQJjpQA8A0shAB6QgAJgGdkAawjV0MZAAkAKgFkAMgFEmecPLgBzAHJw8iuAAcAfAApSZZGAAWwYQC40mXPjAAaa2WGNmrFDOyJzWAJQuCirqEJpg2vqGEMYmANqcALrIAD7IIFgMDNaUNPRMLGxcPPz4IuKS0si0AGoA4jFxCQZGppY+tg7BGNhx3jbIfuWBwaFkEU1tHR5dSSnpWbn5hcVUUHT+FRyq1QJ1SwQAvMjnYH3j9o4uw+7gYzaTAWzBwmBQoDrhFzHTYFIoAX2IxAA9FDkAB5ThOYiCNxxAB0JT2ZU+UAsAHI0TB0Og8WFITDkABhdA4EzAJgAWjAwDwPCgUDYSIQ6BAP2QABJkFcUSMPBjdvspmxiPz8YTiaTybD5INkHh7OhBMg7HBhCA8WBkAAjCD4ZAmOBgBB2CCCbwU4ToZDAMB40QI5CW5Dc2n0lDMvDQ2FGrCGqAQYQFMCiUCe5BQLDgFkoaAcqBcnl8-mC4WosWYyU4gCC22zcqJJLJQA\" class=\"link link-external\">Demo on TS Playground</a></p>\n<h2 id=\"declaration-merging\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#declaration-merging\">Declaration merging</a></h2>\n<p>You could actually copy that partial interface (with only the patched <code>querySelector</code> methods) to your codebase.\nIt would be merged with the default <code>Document</code> interface\nbecause of <a href=\"https://www.typescriptlang.org/docs/handbook/declaration-merging.html\" class=\"link link-external\">declaration merging</a>.</p>\n<p>However, to get comprehensive compile-time safety against &quot;illegal invocation&quot; errors,\nyou would need to:</p>\n<ul>\n<li>Specify the type of the <code>this</code> keyword for <em>all</em> applicable methods of the <code>Document</code> interface.</li>\n<li>Do the same for other interfaces as well, e.g. the <code>AbortController</code> interface.</li>\n</ul>\n<p>This doesn't feel like it belongs to application code.</p>\n<p>Ideally the interfaces would be fixed in TypeScript.\nOr maybe there could be a library that exports patched interfaces\nthat you could import once in your own code.</p>\n<p>Note that in your own methods you can:</p>\n<ul>\n<li>Declare strict types for the <code>this</code> parameter.</li>\n<li>Use better error messages than &quot;illegal invocation.&quot;</li>\n<li>Avoid the <code>this</code> parameter altogether.</li>\n</ul>\n","summary":"The TypeScript compiler could help prevent \"illegal invocation\" errors at compile time,\nat least theoretically.","date_published":"2022-04-17T12:00:00+03:00","tags":["JavaScript","TypeScript"]},{"id":"https://mtsknn.fi/blog/react-links-in-html/","url":"https://mtsknn.fi/blog/react-links-in-html/","title":"Client-side navigation and React's `dangerouslySetInnerHTML`","content_html":"<p>To handle internal link clicks when using <code>dangerouslySetInnerHTML</code>,\nattach click event listeners to the rendered links.\nIn the listener, push the link to the browser history.</p>\n<h2 id=\"overview\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#overview\">Overview</a></h2>\n<ol>\n<li>Render the HTML with <a href=\"https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml\" class=\"link link-external\"><code>dangerouslySetInnerHTML</code></a>.\nRemember to use only safe HTML!</li>\n<li>When the component mounts,\nattach click event listeners to the links.\nIn the event listener,\npush the link to the browser history if it's an internal link.</li>\n<li>When the component unmounts,\nremove the click event listeners to avoid memory leaks.</li>\n</ol>\n<h2 id=\"sample-code\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#sample-code\">Sample code</a></h2>\n<p>Using TypeScript:</p>\n<pre aria-label=\"Code block\" class=\"language-tsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> React<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> RefObject<span class=\"token punctuation\">,</span> useEffect<span class=\"token punctuation\">,</span> useRef <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> useNavigate <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react-router-dom'</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">function</span> <span class=\"token function\">HtmlContent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> html <span class=\"token punctuation\">}</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> html<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token generic-function\"><span class=\"token function\">useRef</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>HTMLDivElement<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n  <span class=\"token function\">useLinkClickHandlers</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">dangerouslySetInnerHTML</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span> __html<span class=\"token operator\">:</span> html <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span></span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">useLinkClickHandlers</span><span class=\"token punctuation\">(</span>ref<span class=\"token operator\">:</span> RefObject<span class=\"token operator\">&lt;</span>HTMLDivElement<span class=\"token operator\">></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> navigate <span class=\"token operator\">=</span> <span class=\"token function\">useNavigate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span>\n\n    <span class=\"token keyword\">const</span> links <span class=\"token operator\">=</span> ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">.</span><span class=\"token function\">querySelectorAll</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">)</span>\n    links<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>link<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> link<span class=\"token punctuation\">.</span><span class=\"token function\">addEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleLinkClick<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      links<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>link<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n        link<span class=\"token punctuation\">.</span><span class=\"token function\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleLinkClick<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">function</span> <span class=\"token function\">handleLinkClick</span><span class=\"token punctuation\">(</span>event<span class=\"token operator\">:</span> MouseEvent<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> link <span class=\"token operator\">=</span> event<span class=\"token punctuation\">.</span>currentTarget <span class=\"token keyword\">as</span> HTMLAnchorElement\n      <span class=\"token keyword\">const</span> href <span class=\"token operator\">=</span> link<span class=\"token punctuation\">.</span><span class=\"token function\">getAttribute</span><span class=\"token punctuation\">(</span><span class=\"token string\">'href'</span><span class=\"token punctuation\">)</span>\n      <span class=\"token keyword\">const</span> target <span class=\"token operator\">=</span> link<span class=\"token punctuation\">.</span><span class=\"token function\">getAttribute</span><span class=\"token punctuation\">(</span><span class=\"token string\">'target'</span><span class=\"token punctuation\">)</span>\n      <span class=\"token keyword\">const</span> url <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token constant\">URL</span></span><span class=\"token punctuation\">(</span>href <span class=\"token operator\">||</span> <span class=\"token string\">''</span><span class=\"token punctuation\">,</span> window<span class=\"token punctuation\">.</span>location<span class=\"token punctuation\">.</span>origin<span class=\"token punctuation\">)</span>\n\n      <span class=\"token keyword\">const</span> isInternalLink <span class=\"token operator\">=</span> url<span class=\"token punctuation\">.</span>origin <span class=\"token operator\">===</span> window<span class=\"token punctuation\">.</span>location<span class=\"token punctuation\">.</span>origin\n      <span class=\"token keyword\">const</span> isOpenedInSameWindow <span class=\"token operator\">=</span> <span class=\"token operator\">!</span>target <span class=\"token operator\">||</span> target <span class=\"token operator\">===</span> <span class=\"token string\">'_self'</span>\n      <span class=\"token keyword\">const</span> isLeftButtonClick <span class=\"token operator\">=</span> event<span class=\"token punctuation\">.</span>button <span class=\"token operator\">===</span> <span class=\"token number\">0</span>\n\n      <span class=\"token comment\">// E.g. Ctrl-clicking a link opens it in a new tab</span>\n      <span class=\"token comment\">// so let the browser handle such clicks</span>\n      <span class=\"token keyword\">const</span> isModifierKeyPressed <span class=\"token operator\">=</span>\n        event<span class=\"token punctuation\">.</span>altKey <span class=\"token operator\">||</span> event<span class=\"token punctuation\">.</span>ctrlKey <span class=\"token operator\">||</span> event<span class=\"token punctuation\">.</span>metaKey <span class=\"token operator\">||</span> event<span class=\"token punctuation\">.</span>shiftKey\n\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>\n        isInternalLink <span class=\"token operator\">&amp;&amp;</span>\n        isOpenedInSameWindow <span class=\"token operator\">&amp;&amp;</span>\n        isLeftButtonClick <span class=\"token operator\">&amp;&amp;</span>\n        <span class=\"token operator\">!</span>isModifierKeyPressed\n      <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        event<span class=\"token punctuation\">.</span><span class=\"token function\">preventDefault</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token function\">navigate</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">.</span>pathname <span class=\"token operator\">+</span> url<span class=\"token punctuation\">.</span>search <span class=\"token operator\">+</span> url<span class=\"token punctuation\">.</span>hash<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>navigate<span class=\"token punctuation\">,</span> ref<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>I'm using <a href=\"https://reactrouter.com/docs/en/v6/api#usenavigate\" class=\"link link-external\">React Router's <code>useNavigate</code> hook</a>.\nIf you don't use React Router,\nyou can do something similar yourself.\n<a href=\"https://github.com/remix-run/react-router/blob/v6.3.0/packages/react-router/lib/hooks.tsx#L185-L188\" class=\"link link-external\">The <code>useNavigate</code> hook pushes the link to the browser history</a>\n(or replaces instead of pushes, depending on how the hook is used).</p>\n<p>The <code>handleLinkClick</code> function is similar to\n<a href=\"https://reactrouter.com/docs/en/v6/api#uselinkclickhandler\" class=\"link link-external\">React Router's <code>useLinkClickHandler</code> hook</a>\n(which can't be used in this case since it takes a link address as a parameter).</p>\n<h3 id=\"usage\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#usage\">Usage</a></h3>\n<pre aria-label=\"Code block\" class=\"language-tsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// This could come from a content management system (CMS) for example</span>\n<span class=\"token keyword\">const</span> myHtml <span class=\"token operator\">=</span> <span class=\"token comment\">/* HTML */</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n  &lt;p>Hello from HTML!&lt;/p>\n  &lt;p>\n    Here's an internal link:\n    &lt;a href=\"/foo\">foo&lt;/a>\n  &lt;/p>\n  &lt;p>\n    Another link:\n    &lt;a href=\"/bar\" target=\"_blank\">bar&lt;/a>\n  &lt;/p>\n</span><span class=\"token template-punctuation string\">`</span></span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">App</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">HtmlContent</span></span> <span class=\"token attr-name\">html</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>myHtml<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>By the way,\nbecause of the <code>/* HTML */</code> comment,\n<a href=\"https://prettier.io/blog/2018/11/07/1.15.0.html#html-template-literal-in-javascript\" class=\"link link-external\">Prettier formats the template literal as HTML</a>.</p>\n<h2 id=\"why-not-attach-a-click-event-listener-to-the-parent\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-not-attach-a-click-event-listener-to-the-parent\">Why not attach a click event listener to the parent?</a></h2>\n<p>Instead of finding all <code>&lt;a&gt;</code> elements and attaching click event listeners to them,\nyou technically <em>could</em> attach a click event listener only to the parent <code>&lt;div&gt;</code>.\nIn the listener function you could then check if the click was done over a link,\nand then handle the link click like in the code above.\nLike so:</p>\n<pre aria-label=\"Code block\" class=\"language-tsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> React<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> RefObject<span class=\"token punctuation\">,</span> useEffect<span class=\"token punctuation\">,</span> useRef <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> useNavigate <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react-router-dom'</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">function</span> <span class=\"token function\">HtmlContent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> html <span class=\"token punctuation\">}</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> html<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> ref <span class=\"token operator\">=</span> <span class=\"token generic-function\"><span class=\"token function\">useRef</span><span class=\"token generic class-name\"><span class=\"token operator\">&lt;</span>HTMLDivElement<span class=\"token operator\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n  <span class=\"token function\">useLinkClickHandler</span><span class=\"token punctuation\">(</span>ref<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">dangerouslySetInnerHTML</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span> __html<span class=\"token operator\">:</span> html <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span></span> <span class=\"token attr-name\">ref</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">useLinkClickHandler</span><span class=\"token punctuation\">(</span>ref<span class=\"token operator\">:</span> RefObject<span class=\"token operator\">&lt;</span>HTMLDivElement<span class=\"token operator\">></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> navigate <span class=\"token operator\">=</span> <span class=\"token function\">useNavigate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span>\n\n    ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">.</span><span class=\"token function\">addEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleLinkClick<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> ref<span class=\"token punctuation\">.</span>current<span class=\"token punctuation\">.</span><span class=\"token function\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleLinkClick<span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">function</span> <span class=\"token function\">handleLinkClick</span><span class=\"token punctuation\">(</span>event<span class=\"token operator\">:</span> MouseEvent<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> link <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">.</span>target <span class=\"token keyword\">as</span> HTMLElement<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">closest</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">)</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>link<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span>\n\n      <span class=\"token comment\">// Handle link click like in the previous example</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>navigate<span class=\"token punctuation\">,</span> ref<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>However, <strong>I don't recommend this</strong>\nbecause it would be <strong>confusing for screen reader users</strong>.</p>\n<p>Attaching a click event listener to a non-interactive element like <code>&lt;div&gt;</code>\nmakes some screen readers (e.g. <a href=\"https://www.nvaccess.org/\" class=\"link link-external\">NVDA</a>)\nannounce the element as &quot;clickable.&quot;</p>\n<p>In this case,\nthe <code>&lt;div&gt;</code> element is not clickable per se\neven though a click handler has been attached to it –\nthe <em>links</em> are clickable, not the <code>&lt;div&gt;</code> element –\nso announcing the <code>&lt;div&gt;</code> as &quot;clickable&quot; is confusing.</p>\n<p>That's way it's better to attach click event listeners to the <code>&lt;a&gt;</code> elements instead.</p>\n<h2 id=\"alternative-approach\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#alternative-approach\">Alternative approach</a></h2>\n<p>You can use a library like <a href=\"https://github.com/remarkablemark/html-react-parser\" class=\"link link-external\">html-react-parser</a> or <a href=\"https://github.com/pveyes/htmr\" class=\"link link-external\">htmr</a>\nto convert the HTML string to React components.\n<code>&lt;a&gt;</code> elements could be converted e.g. to <a href=\"https://reactrouter.com/docs/en/v6/api#link\" class=\"link link-external\">React Router's <code>&lt;Link&gt;</code> components</a>.</p>\n","summary":"To handle internal link clicks when using `dangerouslySetInnerHTML`,\nattach click event listeners to the rendered links.\nIn the listener, push the link to the browser history.","date_published":"2022-04-03T12:00:00+03:00","date_modified":"2022-04-05T12:00:00+03:00","tags":["JavaScript","React","TypeScript"]},{"id":"https://mtsknn.fi/blog/joi-conditional-multiple-values/","url":"https://mtsknn.fi/blog/joi-conditional-multiple-values/","title":"Joi: checking against multiple values in `conditional()`","content_html":"<p>Use <code>{ is: Joi.any().valid(...values) }</code>\nto check if a value is included in an array.</p>\n<h2 id=\"warm-up-problem-no-arrays\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#warm-up-problem-no-arrays\">Warm-up problem (no arrays)</a></h2>\n<ul>\n<li>You have two values: <code>a</code> and <code>b</code>.</li>\n<li>You want to validate the data with these rules:\n<ul>\n<li><code>a</code> is a required string.</li>\n<li>If <code>a</code> equals <code>'foo'</code>,\n<code>b</code> is a required string;\notherwise <code>b</code> is forbidden.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"solution-to-the-warm-up-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution-to-the-warm-up-problem\">Solution to the warm-up problem</a></h3>\n<p>Use <a href=\"https://joi.dev/api/?v=17.6.0#alternativesconditionalcondition-options\" class=\"link link-external\"><code>Joi.alternatives().conditional()</code></a>\nto make the validation of <code>b</code> depend on the value of <code>a</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> Joi <span class=\"token keyword\">from</span> <span class=\"token string\">'joi'</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> schema <span class=\"token operator\">=</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">object</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">a</span><span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">required</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">b</span><span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">alternatives</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">conditional</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">is</span><span class=\"token operator\">:</span> <span class=\"token string\">'foo'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">then</span><span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">required</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">otherwise</span><span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">forbidden</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>I'm using <a href=\"https://joi.dev/api/?v=17.6.0\" class=\"link link-external\">Joi 17.6.0</a> by the way.</p>\n<h2 id=\"array-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#array-problem\">Array problem</a></h2>\n<p>The same problem as above,\nbut instead of checking if <code>a</code> equals <code>'foo'</code>,\nwe want to check if <code>a</code> is included in an array:</p>\n<ul>\n<li>You have two values: <code>a</code> and <code>b</code>.</li>\n<li>You want to validate the data with these rules:\n<ul>\n<li><code>a</code> is a required string.</li>\n<li><strong>If <code>a</code> is included in <code>array</code>,</strong>\n<code>b</code> is a required string;\notherwise <code>b</code> is forbidden.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"solution-to-the-array-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution-to-the-array-problem\">Solution to the array problem</a></h3>\n<p>We need to change the <code>is</code> condition.</p>\n<p>To check against multiple values,\nuse <a href=\"https://joi.dev/api/?v=17.6.0#anyvalidvalues---aliases-equal\" class=\"link link-external\"><code>Joi.any().valid()</code> (alias: <code>Joi.any().equal()</code>)</a>,\nwhich &quot;adds the provided values into the allowed values list\nand marks them as the only valid values allowed&quot;:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> Joi <span class=\"token keyword\">from</span> <span class=\"token string\">'joi'</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> array <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'foo'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'bar'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'baz'</span><span class=\"token punctuation\">]</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> schema <span class=\"token operator\">=</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">object</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">a</span><span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">required</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">b</span><span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">alternatives</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">conditional</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">is</span><span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">any</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">valid</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span>array<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">then</span><span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">required</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">otherwise</span><span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">forbidden</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>In this example case\nit's even better to use <code>string()</code> instead of <code>any()</code>\nto clarify the intent of the schema:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token literal-property property\">is</span><span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">any</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">valid</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span>array<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token literal-property property\">is</span><span class=\"token operator\">:</span> Joi<span class=\"token punctuation\">.</span><span class=\"token function\">string</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">valid</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span>array<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n</span></span></code></pre>\n<h2 id=\"unit-tests\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#unit-tests\">Unit tests</a></h2>\n<p>I wanted to be sure that my conditional Joi schema works,\nso I tested it like this using Jest:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> array<span class=\"token punctuation\">,</span> schema <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'./schema'</span>\n\n<span class=\"token function\">describe</span><span class=\"token punctuation\">(</span><span class=\"token string\">'schema'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  test<span class=\"token punctuation\">.</span><span class=\"token function\">each</span><span class=\"token punctuation\">(</span>array<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token string\">'requires `b` if `a` is %s'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">a</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> validationResult <span class=\"token operator\">=</span> schema<span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> a<span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">b</span><span class=\"token operator\">:</span> <span class=\"token keyword\">undefined</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">const</span> errorMessage <span class=\"token operator\">=</span> validationResult<span class=\"token punctuation\">.</span>error<span class=\"token operator\">?.</span>details<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>message\n    <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>errorMessage<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toBe</span><span class=\"token punctuation\">(</span><span class=\"token string\">'\"b\" is required'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  test<span class=\"token punctuation\">.</span><span class=\"token function\">each</span><span class=\"token punctuation\">(</span>array<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token string\">'accepts `b` if `a` is %s'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">a</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> validationResult <span class=\"token operator\">=</span> schema<span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> a<span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">b</span><span class=\"token operator\">:</span> <span class=\"token string\">'fleebles'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>validationResult<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>not<span class=\"token punctuation\">.</span><span class=\"token function\">toHaveProperty</span><span class=\"token punctuation\">(</span><span class=\"token string\">'error'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">it</span><span class=\"token punctuation\">(</span><span class=\"token string\">'forbids `b` if `a` is some other value'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> validationResult <span class=\"token operator\">=</span> schema<span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">a</span><span class=\"token operator\">:</span> <span class=\"token string\">'nibbles'</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">b</span><span class=\"token operator\">:</span> <span class=\"token string\">'fleebles'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">const</span> errorMessage <span class=\"token operator\">=</span> validationResult<span class=\"token punctuation\">.</span>error<span class=\"token operator\">?.</span>details<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>message\n    <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>errorMessage<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toBe</span><span class=\"token punctuation\">(</span><span class=\"token string\">'\"b\" is not allowed'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">it</span><span class=\"token punctuation\">(</span><span class=\"token string\">'accepts missing `b` if `a` is some other value'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> validationResult <span class=\"token operator\">=</span> schema<span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">a</span><span class=\"token operator\">:</span> <span class=\"token string\">'nibbles'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>validationResult<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>not<span class=\"token punctuation\">.</span><span class=\"token function\">toHaveProperty</span><span class=\"token punctuation\">(</span><span class=\"token string\">'error'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">it</span><span class=\"token punctuation\">(</span><span class=\"token string\">'accepts undefined `b` if `a` is some other value'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> validationResult <span class=\"token operator\">=</span> schema<span class=\"token punctuation\">.</span><span class=\"token function\">validate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">a</span><span class=\"token operator\">:</span> <span class=\"token string\">'nibbles'</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">b</span><span class=\"token operator\">:</span> <span class=\"token keyword\">undefined</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>validationResult<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>not<span class=\"token punctuation\">.</span><span class=\"token function\">toHaveProperty</span><span class=\"token punctuation\">(</span><span class=\"token string\">'error'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Result of a test run:</p>\n<pre aria-label=\"Code block\" class=\"language-plain bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">$ NODE_OPTIONS=--experimental-vm-modules npx jest\nPASS  ./schema.test.js\n  schema\n    ✓ requires `b` if `a` is foo\n    ✓ requires `b` if `a` is bar\n    ✓ requires `b` if `a` is baz\n    ✓ accepts `b` if `a` is foo\n    ✓ accepts `b` if `a` is bar\n    ✓ accepts `b` if `a` is baz\n    ✓ forbids `b` if `a` is some other value\n    ✓ accepts missing `b` if `a` is some other value\n    ✓ accepts undefined `b` if `a` is some other value\n\nTest Suites: 1 passed, 1 total\nTests:       9 passed, 9 total\nSnapshots:   0 total\nTime:        0.12 s, estimated 1 s\nRan all test suites.\n</span></code></pre>\n<p>The tests are a bit questionable:</p>\n<ul>\n<li>We depend on specific error messages.\nWhat if they change when Joi is updated?</li>\n<li>We are almost testing that Joi works like it should.\nShould we be able to trust that\n<code>Joi.alternatives().conditional()</code> just works?</li>\n</ul>\n<p>Nonetheless,\nI wanted to be sure that my schema works\nand these tests do the job for now.</p>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<p>Joi:</p>\n<ul>\n<li><a href=\"https://joi.dev/api/?v=17.6.0#alternativesconditionalcondition-options\" class=\"link link-external\"><code>Joi.alternatives().conditional()</code></a></li>\n<li><a href=\"https://joi.dev/api/?v=17.6.0#anyvalidvalues---aliases-equal\" class=\"link link-external\"><code>Joi.any().valid()</code> (alias: <code>Joi.any().equal()</code>)</a></li>\n</ul>\n<p>Jest:</p>\n<ul>\n<li><a href=\"https://jestjs.io/docs/api#testeachtablename-fn-timeout\" class=\"link link-external\"><code>test.each()</code></a></li>\n<li><a href=\"https://jestjs.io/docs/expect#tohavepropertykeypath-value\" class=\"link link-external\"><code>expect().toHaveProperty()</code></a></li>\n<li><a href=\"https://jestjs.io/docs/ecmascript-modules\" class=\"link link-external\">Using ES Modules with Jest</a>\n<ul>\n<li>You might have noticed above that I had to use\n<code>NODE_OPTIONS=--experimental-vm-modules npx jest</code>\ninstead of just <code>npx jest</code>.</li>\n</ul>\n</li>\n</ul>\n","summary":"Use `{ is: Joi.any().valid(...values) }`\nto check if a value is included in an array.","date_published":"2022-03-09T12:00:00+03:00","date_modified":"2022-03-18T12:00:00+03:00","tags":["JavaScript","Joi","Testing"]},{"id":"https://mtsknn.fi/blog/mts-file-extension/","url":"https://mtsknn.fi/blog/mts-file-extension/","title":"`.mts` is a cool file extension (TypeScript ES modules)","content_html":"<p>What's that, a file extension named after me?!\nOr a file extension for TypeScript files that are always ES modules?</p>\n<p>I was reading the\n<a href=\"https://prettier.io/blog/2021/11/25/2.5.0.html\" class=\"link link-external\">Prettier 2.5 announcement post</a>\nthe other day\nand noticed that\n&quot;Prettier will now format files with <code>.mts</code> and <code>.cts</code> extensions as TypeScript.&quot;</p>\n<p>Turns out <code>.mts</code> is not named after my first name (Matias).\nIt's for TypeScript files that are always ES modules.</p>\n<ul>\n<li>ES modules (ESM) are JavaScript or TypeScript files\nthat use the <code>import</code>/<code>export</code> syntax.</li>\n<li>CommonJS (CJS) modules are JavaScript or TypeScript files\nthat use the <code>require()</code>/<code>module.exports</code> syntax.\n<ul>\n<li><a href=\"https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require\" class=\"link link-external\">TypeScript might require special syntax for CommonJS modules</a>\n(I'm not sure).</li>\n</ul>\n</li>\n</ul>\n<p>Usually JavaScript files use the <code>.js</code> extension\nand TypeScript files use the <code>.ts</code> extension.\nSuch files can be either ES modules or CommonJS modules.</p>\n<p>The <a href=\"https://devblogs.microsoft.com/typescript/announcing-typescript-4-5-beta/\" class=\"link link-external\">TypeScript 4.5 Beta announcement post</a>\nexplains when the more precise file extensions can be useful\n(emphasis added):</p>\n<blockquote>\n<p>Node.js supports\n<a href=\"https://nodejs.org/api/packages.html#packages_package_json_and_file_extensions\" class=\"link link-external\">a new setting in <code>package.json</code> called <code>type</code></a>.\n<code>&quot;type&quot;</code> can be set to either <code>&quot;module&quot;</code> or <code>&quot;commonjs&quot;</code>.</p>\n<p>This setting controls whether <code>.js</code> files are interpreted as ES modules or CommonJS modules,\nand defaults to CommonJS when not set.</p>\n<p>[...]</p>\n<p>The <code>type</code> field in <code>package.json</code> is nice\nbecause it allows us to continue using the <code>.ts</code> and <code>.js</code> file extensions which can be convenient;\nhowever, <strong>you will occasionally need to write a file that differs from what <code>type</code> specifies.\nYou might also just prefer to always be explicit.</strong></p>\n<p>Node.js supports two extensions to help with this: <code>.mjs</code> and <code>.cjs</code>.\n<code>.mjs</code> files are always ES modules,\nand <code>.cjs</code> files are always CommonJS modules,\nand there's no way to override these.</p>\n<p>In turn,\nTypeScript supports two new source file extensions:\n<code>.mts</code> and <code>.cts</code>.\nWhen TypeScript emits these to JavaScript files,\nit will emit them to <code>.mjs</code> and <code>.cjs</code> respectively.</p>\n</blockquote>\n<p>There are more details in the first section of the\n<a href=\"https://devblogs.microsoft.com/typescript/announcing-typescript-4-5-beta/\" class=\"link link-external\">TypeScript 4.5 Beta announcement post</a>.</p>\n<p>Some tools may also require or encourage the more precise file extensions.\nAn analogy:\n<a href=\"https://vitejs.dev/\" class=\"link link-external\">Vite (a build tool)</a>\nsupports JSX by default only in <code>.jsx</code> and <code>.tsx</code> files\n(but not in <code>.js</code> and <code>.ts</code> files)\n<a href=\"https://github.com/vitejs/vite/issues/769#issuecomment-780593283\" class=\"link link-external\">for performance reasons</a>.</p>\n<p>Can you think of a file extension that's named after you?</p>\n","summary":"What's that, a file extension named after me?!\nOr a file extension for TypeScript files that are always ES modules?","date_published":"2022-02-09T12:00:00+03:00","tags":["JavaScript","Node.js","TypeScript"]},{"id":"https://mtsknn.fi/blog/react-is-a-framework/","url":"https://mtsknn.fi/blog/react-is-a-framework/","title":"React is a framework, not a library","content_html":"<p>React is marketed as a view library,\nbut it's more accurately a (view component) framework.</p>\n<h2 id=\"react-is-a-framework-because-its-in-control-of-when-your-code-runs\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#react-is-a-framework-because-its-in-control-of-when-your-code-runs\">React is a framework because it's in control of when your code runs</a></h2>\n<p>A snippet from <a href=\"https://gist.github.com/peerreynders/b1e30a4950ca7eed9b2f547f1bf2f151\" class=\"link link-external\">a GitHub gist by Peer Reynders</a>\n(with minor grammatical edits by me):</p>\n<blockquote>\n<p>The litmus test:</p>\n<ul>\n<li>If <em>your code calls it</em>, it's <strong>a library</strong>.</li>\n<li>If <em>it calls your code</em>, it's <strong>a framework</strong>.</li>\n</ul>\n<p>Looking at the React tutorial's<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup> Starter Code,\n<code>ReactDOM.render()</code> is the only part of React that <strong>your code calls</strong>.\nThe rest, i.e. the majority of your code, <strong>is called by React</strong>.</p>\n<p>Ipso facto<sup><a aria-label=\"footnote 2\" class=\"link\" href=\"#fn-2\" id=\"fnref-2\">[2]</a></sup> – <strong>React is a framework</strong>\nbecause it is in control when most of your code runs.</p>\n</blockquote>\n<p>There you have it.\nI have thought for some time that React is a framework, not a library,\nbut I haven't had the words to explain why.</p>\n<h2 id=\"react-vs-jquery\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#react-vs-jquery\">React vs jQuery</a></h2>\n<p>It becomes clearer from Peer's comparison of jQuery (a library) and React (a framework).\nBoth are <em>tools</em> for dealing with the DOM,\nbut they are used differently:</p>\n<blockquote>\n<p>jQuery for the most part is used as a layer between your code and the browser.\nYour code calls jQuery to fulfill some objective –\nand when it's done, it's done.\njQuery doesn't do anything until your code calls it the next time.\nSome jQuery code may run due to being referenced in an event handler\nor because of an async callback,\nbut that is just in the nature of browser operations.\njQuery as such isn't in control, your code is.\njQuery is called a <em>library</em> because it's simply a collection of useful helpers;\nafter you call each method, your code is firmly back in charge.</p>\n<p>With <code>ReactDOM.render()</code> your code hands over control to React.\nPast that point React is in full control\nand your code only runs when React decides to run it.\nReact isn't a layer that your code calls to deal with the nitty-gritty of the browser,\nbut an environment that isolates your components from the browser\nand uses your components when it deems it necessary –\nwhich is exactly what a framework does.</p>\n<p>[...]</p>\n<p>So both jQuery and React stand between your code and the browser('s DOM),\nbut your code's pattern of interaction with them is <em>very different</em>:</p>\n<ul>\n<li>jQuery is a collection of helpers that your code calls whenever it needs to.</li>\n<li>React on the other hand calls your code. [...]</li>\n</ul>\n</blockquote>\n<p><a href=\"https://gist.github.com/peerreynders/b1e30a4950ca7eed9b2f547f1bf2f151\" class=\"link link-external\">Go read Peer's GitHub gist</a>;\nit has more great points but isn't too long.</p>\n<h2 id=\"does-it-matter\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#does-it-matter\">Does it matter?</a></h2>\n<p>Does it matter whether React is marketed and thought of as a framework or a library?</p>\n<p>Should React embrace the fact that it's a framework?\nWould it affect <a href=\"/blog/reacts-ecosystem-is-a-dumb-necessity/\" class=\"link\">React's ecosystem</a>?</p>\n<p>Good questions.\nWhat do <em>you</em> think?\nSend me an email (my address is on the <a href=\"/\" class=\"link\">home page</a>)\nand let me know!</p>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>Peer is referring to the\n<a href=\"https://reactjs.org/tutorial/tutorial.html\" class=\"link link-external\">React tutorial on reactjs.org</a>. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n<li id=\"fn-2\"><p><a href=\"https://en.wikipedia.org/wiki/Ipso_facto\" class=\"link link-external\">Wikipedia's definition of <em>ipso facto</em></a>:</p>\n<blockquote>\n<p><em><strong>Ipso facto</strong></em> is a Latin phrase,\ndirectly translated as &quot;by the fact itself,&quot;\nwhich means that a specific phenomenon is a <em>direct</em> consequence,\na resultant <em>effect</em>,\nof the action in question,\ninstead of being brought about by a previous action.\nIt is a term of art used in philosophy, law, and science.</p>\n</blockquote>\n <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-2\">↩</a></li>\n</ol>\n</section>\n","summary":"React is marketed as a view library,\nbut it's more accurately a (view component) framework.","date_published":"2021-10-12T12:00:00+03:00","tags":["JavaScript","React"]},{"id":"https://mtsknn.fi/blog/why-compose-is-right-to-left/","url":"https://mtsknn.fi/blog/why-compose-is-right-to-left/","title":"Why `compose()` is right-to-left","content_html":"<p>Functions composed together with <code>compose()</code>\nare called from right to left.\nIt feels unintuitive at first,\nbut it's conventional\nand kind of makes sense.</p>\n<h2 id=\"whats-compose\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#whats-compose\">What's <code>compose()</code>?</a></h2>\n<p>I was recently reading the book\n<a href=\"https://mostly-adequate.gitbook.io/mostly-adequate-guide/\" class=\"link link-external\"><em>Professor Frisby's Mostly Adequate Guide to Functional Programming [in JavaScript]</em></a>.<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup>\n(It's a good book,\nbut became too much for me somewhere around chapter 8 or 9.\n<span aria-hidden=\"true\" class=\"whitespace-no-wrap\">¯\\(ツ)/¯</span>)</p>\n<p><a href=\"https://mostly-adequate.gitbook.io/mostly-adequate-guide/ch05\" class=\"link link-external\">Chapter 5, <em>Coding by Composing</em></a>,\nintroduces a <code>compose()</code> function for composing functions together:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">compose</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token operator\">...</span>fns</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token operator\">...</span>args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  fns<span class=\"token punctuation\">.</span><span class=\"token function\">reduceRight</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">res<span class=\"token punctuation\">,</span> fn</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">[</span><span class=\"token function\">fn</span><span class=\"token punctuation\">.</span><span class=\"token function\">call</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>res<span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> args<span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span>\n</span></code></pre>\n<p>(There's also <a href=\"https://ramdajs.com/docs/#compose\" class=\"link link-external\">Ramda's <code>R.compose</code></a>,\nand <a href=\"https://lodash.com/docs/#flowRight\" class=\"link link-external\">Lodash's <code>_.flowRight</code></a>\nwhich is aliased to <code>_.compose</code> in\n<a href=\"https://github.com/lodash/lodash/wiki/FP-Guide\" class=\"link link-external\"><code>lodash/fp</code></a>.)</p>\n<p>Example usage from the book:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">toUpperCase</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">x</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> x<span class=\"token punctuation\">.</span><span class=\"token function\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">exclaim</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">x</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>x<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">!</span><span class=\"token template-punctuation string\">`</span></span>\n<span class=\"token keyword\">const</span> shout <span class=\"token operator\">=</span> <span class=\"token function\">compose</span><span class=\"token punctuation\">(</span>exclaim<span class=\"token punctuation\">,</span> toUpperCase<span class=\"token punctuation\">)</span>\n\n<span class=\"token function\">shout</span><span class=\"token punctuation\">(</span><span class=\"token string\">'send in the clowns'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// \"SEND IN THE CLOWNS!\"</span>\n</span></code></pre>\n<p><code>shout</code> is a function composed of the <code>exclaim</code> and <code>toUpperCase</code> functions:\nit first calls <code>toUpperCase</code> and then <code>exclaim</code>.</p>\n<p>Calling <code>shout</code> is the same as calling the two functions &quot;manually&quot;\n(without using <code>compose</code>):</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">exclaim</span><span class=\"token punctuation\">(</span><span class=\"token function\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token string\">'send in the clowns'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// \"SEND IN THE CLOWNS!\"</span>\n</span></code></pre>\n<p>The order of the two functions doesn't matter in this case\nbecause the end result is the same with either order.</p>\n<p>Another example (a bit contrived) from the book;\nhere the order does matter:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">head</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">x</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> x<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span>\n<span class=\"token keyword\">const</span> reverse <span class=\"token operator\">=</span> <span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">acc<span class=\"token punctuation\">,</span> x</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">[</span>x<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>acc<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> last <span class=\"token operator\">=</span> <span class=\"token function\">compose</span><span class=\"token punctuation\">(</span>head<span class=\"token punctuation\">,</span> reverse<span class=\"token punctuation\">)</span>\n\n<span class=\"token function\">last</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'jumpkick'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'roundhouse'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'uppercut'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// 'uppercut'</span>\n</span></code></pre>\n<p><code>last</code> is a function composed of the <code>head</code> and <code>reverse</code> functions:\nit first calls <code>reverse</code> and then <code>head</code>.</p>\n<p>Again, calling <code>last</code> is the same as calling the two functions &quot;manually&quot;:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">head</span><span class=\"token punctuation\">(</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'jumpkick'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'roundhouse'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'uppercut'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// 'uppercut'</span>\n</span></code></pre>\n<p>The order of the two functions matters in this case:\ncalling <code>head</code> before <code>reverse</code> wouldn't work at all because <code>reverse</code> expects an array.\n(If <code>reverse</code> worked with strings as well,\nthe result would be <code>'kcikpmuj'</code>,\nwhich is totally different from <code>'uppercut'</code>.)</p>\n<h2 id=\"why-right-to-left-is-unintuitive\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-right-to-left-is-unintuitive\">Why right-to-left is unintuitive</a></h2>\n<p>When you encounter <code>compose()</code>,\nyou have to read its arguments backwards.\nJavaScript, like most human languages, is written from left to right,\nso having to read the arguments backwards is unintuitive.</p>\n<p>When you encounter <code>const myFn = compose(many, args, here)</code>,\nyou have to jump to the last argument of <code>compose</code>\nto see what's the first function that <code>myFn</code> calls,\nand then backtrack to the left one argument at a time.</p>\n<p>So, <code>compose(A, B, C)</code> does not mean &quot;first call <code>A</code>, then <code>B</code> and then <code>C</code>&quot; like one could expect.\nIt's the opposite: &quot;first call <code>C</code>, then <code>B</code> and then <code>A</code>.&quot; 🤸‍♂️</p>\n<p>Compare also with method chaining, which works from left to right:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token string\">'send in the clowns'</span><span class=\"token punctuation\">.</span><span class=\"token function\">toUpperCase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">exclaim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// vs</span>\n<span class=\"token function\">compose</span><span class=\"token punctuation\">(</span>exclaim<span class=\"token punctuation\">,</span> toUpperCase<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token string\">'send in the clowns'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token punctuation\">[</span><span class=\"token string\">'jumpkick'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'roundhouse'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'uppercut'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">head</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// vs</span>\n<span class=\"token function\">compose</span><span class=\"token punctuation\">(</span>head<span class=\"token punctuation\">,</span> reverse<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'jumpkick'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'roundhouse'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'uppercut'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h2 id=\"why-right-to-left-makes-sense-kind-of\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-right-to-left-makes-sense-kind-of\">Why right-to-left makes sense (kind of)</a></h2>\n<p>The book justifies the right-to-left order very shallowly:</p>\n<blockquote>\n<p>We could define a left to right version,\nhowever,\nwe mirror the mathematical version much more closely as it stands.\nThat's right,\ncomposition is straight from the math books.</p>\n</blockquote>\n<p>It's been too long since high school math,\nso I had to look up\n<a href=\"https://en.wikipedia.org/wiki/Function_composition\" class=\"link link-external\"><em>Function composition (mathematical concept)</em> on Wikipedia</a>.\nAn example<sup><a aria-label=\"footnote 2\" class=\"link\" href=\"#fn-2\" id=\"fnref-2\">[2]</a></sup>:</p>\n<blockquote>\n<p>(𝑓∘𝑔∘ℎ)(𝑧) = 𝑓(𝑔(ℎ(𝑧)))</p>\n</blockquote>\n<p>Notice how the order of the functions 𝑓, 𝑔 and ℎ\nis the same on both sides of the equals sign.</p>\n<p>This is true in JavaScript as well:\n<code>compose(A, B, C)(arg)</code> is the same as <code>A(B(C(arg)))</code>.\nPut more visually:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">compose</span><span class=\"token punctuation\">(</span><span class=\"token constant\">A</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">B</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">C</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span>arg<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//      ↓  ↓  ↓  ↓↓↓</span>\n        <span class=\"token constant\">A</span><span class=\"token punctuation\">(</span> <span class=\"token constant\">B</span><span class=\"token punctuation\">(</span> <span class=\"token constant\">C</span><span class=\"token punctuation\">(</span> arg <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>If you think of <code>compose()</code> this way,\nit <em>kind of</em> makes sense.</p>\n<h2 id=\"pipe-is-left-to-right-and-more-intuitive\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#pipe-is-left-to-right-and-more-intuitive\"><code>pipe()</code> is left-to-right and more intuitive</a></h2>\n<p>A &quot;left-to-right <code>compose()</code>&quot; is called <code>pipe()</code>.</p>\n<p>For example,\n<a href=\"https://ramdajs.com/docs/#pipe\" class=\"link link-external\">Ramda has <code>R.pipe</code></a>,\nand <a href=\"https://lodash.com/docs/#flow\" class=\"link link-external\">Lodash has <code>_.flow</code></a>\nwhich is aliased to <code>_.pipe</code> in\n<a href=\"https://github.com/lodash/lodash/wiki/FP-Guide\" class=\"link link-external\"><code>lodash/fp</code></a>.\nThere's also an\n<a href=\"https://github.com/tc39/proposal-pipeline-operator\" class=\"link link-external\">ECMAScript proposal for adding a pipe/pipeline operator (<code>|&gt;</code>) to JavaScript</a>.</p>\n<p>An example from Ramda's documentation:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> f <span class=\"token operator\">=</span> <span class=\"token constant\">R</span><span class=\"token punctuation\">.</span><span class=\"token function\">pipe</span><span class=\"token punctuation\">(</span>Math<span class=\"token punctuation\">.</span>pow<span class=\"token punctuation\">,</span> <span class=\"token constant\">R</span><span class=\"token punctuation\">.</span>negate<span class=\"token punctuation\">,</span> <span class=\"token constant\">R</span><span class=\"token punctuation\">.</span>inc<span class=\"token punctuation\">)</span>\n<span class=\"token function\">f</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token number\">4</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// -(3^4) + 1</span>\n</span></code></pre>\n<p>Compare with <code>R.compose</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> f <span class=\"token operator\">=</span> <span class=\"token constant\">R</span><span class=\"token punctuation\">.</span><span class=\"token function\">compose</span><span class=\"token punctuation\">(</span><span class=\"token constant\">R</span><span class=\"token punctuation\">.</span>inc<span class=\"token punctuation\">,</span> <span class=\"token constant\">R</span><span class=\"token punctuation\">.</span>negate<span class=\"token punctuation\">,</span> Math<span class=\"token punctuation\">.</span>pow<span class=\"token punctuation\">)</span>\n<span class=\"token function\">f</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token number\">4</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// -(3^4) + 1</span>\n</span></code></pre>\n<p>Piping is so much more intuitive that\nI wonder what are the arguments for favoring <code>compose</code>.\n&quot;That's how it works in math&quot; is one argument,\nbut how good an argument is it?\nIf you know other arguments,\nplease tell me!</p>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>The book is licensed under a\n<a href=\"https://creativecommons.org/licenses/by-sa/4.0/\" class=\"link link-external\">CC BY-SA 4.0 license</a>.\nSome code samples on this page are from the book;\nI have formatted them with Prettier. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n<li id=\"fn-2\"><p>I was wondering why I couldn't find a Unicode symbol for\n&quot;mathematical italic small h&quot; (ℎ)\neven though I could find symbols for\n&quot;mathematical italic small f&quot; (𝑓)\nand\n&quot;mathematical italic small g&quot; (𝑔).\nApparently because\nwhen the Unicode symbols for mathematical italic small letters were defined,\nℎ had already a different name for it: &quot;Planck constant.&quot;\nInteresting!\nSource:\n<a href=\"https://stackoverflow.com/q/47206070/1079869\" class=\"link link-external\"><em>Why are there holes in the Unicode table?</em> on Stack Overflow</a>. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-2\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"Functions composed together with `compose()`\nare called from right to left.\nIt feels unintuitive at first,\nbut it's conventional\nand kind of makes sense.","date_published":"2021-09-28T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/periods-and-commas-belong-inside-quotes-in-american-english/","url":"https://mtsknn.fi/blog/periods-and-commas-belong-inside-quotes-in-american-english/","title":"Periods and commas belong inside quotes in American English","content_html":"<p>Like so: &quot;The name's Bourne,&quot; the man said, &quot;JSON Bourne.&quot;\nAlso when quoting single words,\nlike &quot;nibbles,&quot; &quot;gronk&quot; and &quot;fleebles.&quot;</p>\n<p>Why?\nBecause it's a rule.\nSome sources:</p>\n<ul>\n<li><a href=\"https://www.thepunctuationguide.com/quotation-marks.html\" class=\"link link-external\"><em>Quotation marks</em> on The Punctuation Guide</a></li>\n<li><a href=\"https://grammar.yourdictionary.com/grammar/punctuation/does-punctuation-go-inside-quotation-marks.html\" class=\"link link-external\"><em>Does Punctuation Go Inside Quotation Marks?</em> on YourDictionary</a></li>\n<li><a href=\"https://style.mla.org/punctuation-and-quotation-marks/\" class=\"link link-external\"><em>Why do periods and commas go inside quotation marks in MLA style?</em> on MLA Style Center</a></li>\n</ul>\n<p>If you are not convinced,\nconsult your favorite style guide.</p>\n<p>I write mainly American English.\nI avoid mixing in other flavors, like British English,\nto keep my English consistent.</p>\n<h2 id=\"an-exception-to-the-rule\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#an-exception-to-the-rule\">An exception to the rule</a></h2>\n<p>Place periods and commas outside the quotes\nif placing them inside the quotes\ncould make the text ambiguous or lead to errors,\ne.g. when quoting user inputs.</p>\n<p>For example:</p>\n<blockquote>\n<p>The password is &quot;hunter2.&quot;</p>\n</blockquote>\n<p>The above is ambiguous:\ndoes the password include the period or not?</p>\n<p>Compare with this:</p>\n<blockquote>\n<p>The password is &quot;hunter2&quot;.</p>\n</blockquote>\n<p>The above is unambiguous and thus preferable.</p>\n<p>Sometimes it's better to change the sentence structure to avoid this problem.</p>\n<p>I don't know if this exception is actually allowed in American English,\nbut it's common sense.</p>\n","summary":"Like so: \"The name's Bourne,\" the man said, \"JSON Bourne.\"\nAlso when quoting single words,\nlike \"nibbles,\" \"gronk\" and \"fleebles.\"","date_published":"2021-09-25T12:00:00+03:00","tags":["English"]},{"id":"https://mtsknn.fi/blog/reacts-ecosystem-is-a-dumb-necessity/","url":"https://mtsknn.fi/blog/reacts-ecosystem-is-a-dumb-necessity/","title":"React's ecosystem is a dumb necessity","content_html":"<p>React's ecosystem is large,\nwhich is a good thing:\nlot's of great libraries to use.\nBut it's also a bad thing:\nmost of those libraries are compatible only with React.</p>\n<p><a href=\"https://dev.to/jfbrennan/comment/1712b\" class=\"link link-external\">Jordan Brennan's comment on dev.to</a>\nbeautifully sums up the downsides:</p>\n<blockquote>\n<p>React's ecosystem is not a benefit.\nIt's a dumb necessity grown from the need to create more stuff,\nmuch of it being React-only versions of existing stuff,\nbecause React is so incompatible and fussy.</p>\n</blockquote>\n<p>And part of <a href=\"https://dev.to/jfbrennan/comment/1h007\" class=\"link link-external\">Jordan's another comment</a>\nas well:</p>\n<blockquote>\n<p>A large ecosystem is not a benefit.\nIt is a sign that the library is too finicky and non-standard\nto play nice with the thousands of other libraries out there,\nhence all the special React versions of those libraries.</p>\n</blockquote>\n<p>In other words:\nintegrating vanilla JS libraries with React is difficult,\nso people create React-only libraries (<code>react-this</code>, <code>react-that</code> etc.).</p>\n<p>What I'd prefer:\nintegrating vanilla JS libraries with React is easy,\nso people don't need to create React-only libraries.\nMost effort and focus can be put to creating and improving vanilla JS libraries,\nand those libraries can easily be used with <em>and without</em> React.\nWin-win!</p>\n<hr>\n<p><a href=\"https://mithril.js.org/\" class=\"link link-external\">Mithril.js</a>,\na front-end framework with some similarities to React,\ntakes a different approach.\nIts documentation has a dedicated (though currently very brief) page about\n<a href=\"https://mithril.js.org/integrating-libs.html\" class=\"link link-external\">integrating 3rd party libraries/code with Mithril.js</a>.</p>\n<p>It would be nice if React had a similar mindset and similar capabilities.</p>\n","summary":"React's ecosystem is large,\nwhich is a good thing:\nlot's of great libraries to use.\nBut it's also a bad thing:\nmost of those libraries are compatible only with React.","date_published":"2021-09-15T12:00:00+03:00","date_modified":"2021-10-12T12:00:00+03:00","tags":["JavaScript","Mithril.js","Rant","React"]},{"id":"https://mtsknn.fi/blog/scheduling-vacations-for-maximal-relaxation/","url":"https://mtsknn.fi/blog/scheduling-vacations-for-maximal-relaxation/","title":"Scheduling vacations for maximal relaxation","content_html":"<p>Try to schedule vacations\nso that they start and end in the middle of the week.\nYou'll get two short work weeks around the vacation.</p>\n<p>If your vacation starts in the middle of the week,\nthe work week before the vacation will be a short one,\nso you won't be totally exhausted on the first day (Saturday) of the vacation.</p>\n<p>If your vacation ends in the middle of the week,\nthe first work week after the vacation will be more pleasant\nas it will be a short one.\nReturning from vacation won't be as stressful\nas you know you'll have to endure only two or three days before the weekend.</p>\n<p>&quot;Endure&quot; only two or three days...?\nMaximize laziness\nand minimize work...?</p>\n<p>Well yes, but actually no.</p>\n<p>You see,\na relaxed worker\nis a happy and productive worker.\nWin-win!</p>\n<p>I learned this trick some years ago from my colleague.\nThanks, Joonas!</p>\n","summary":"Try to schedule vacations\nso that they start and end in the middle of the week.\nYou'll get two short work weeks around the vacation.","date_published":"2021-08-27T12:00:00+03:00","tags":["Work–life balance"]},{"id":"https://mtsknn.fi/blog/jsx-vs-htm-hyperscript-tagged-markup/","url":"https://mtsknn.fi/blog/jsx-vs-htm-hyperscript-tagged-markup/","title":"JSX vs HTM (Hyperscript Tagged Markup)","content_html":"<p>HTM provides a transpiler-free alternative to JSX via tagged templates.\nHTM has some limitations,\nbut can be used in browsers\nand can be good for smaller projects.</p>\n<h2 id=\"jsx\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#jsx\">JSX</a></h2>\n<p>JSX is a syntax extension to JavaScript.\nIt's transpiled away before it's served to the browser.</p>\n<p>For example:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// JSX:</span>\n<span class=\"token keyword\">return</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>foo<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">bar</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n\n<span class=\"token comment\">// Output:</span>\n<span class=\"token keyword\">return</span> React<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'div'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">className</span><span class=\"token operator\">:</span> <span class=\"token string\">'foo'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'bar'</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>JSX is the default choice in like 99% of React projects.\nIt's not a pro or a con per se,\nbut it raises the question:\nwhy wouldn't I just use JSX instead of some alternative?</p>\n<h3 id=\"jsx-pros\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#jsx-pros\">JSX pros</a></h3>\n<h4>Great editor support</h4>\n<ul>\n<li>Syntax highlighting.</li>\n<li>IntelliSense, with or without TypeScript.</li>\n</ul>\n<h4>Syntax errors are actual errors</h4>\n<p>The transpiler won't let syntax errors pass.\nThis is better than HTM failing silently on errors.</p>\n<h4>ESLint plugins</h4>\n<ul>\n<li><a href=\"https://github.com/yannickcr/eslint-plugin-react\" class=\"link link-external\">eslint-plugin-react: React-specific rules</a></li>\n<li><a href=\"https://github.com/jsx-eslint/eslint-plugin-jsx-a11y\" class=\"link link-external\">eslint-plugin-jsx-a11y: accessibility rules</a></li>\n</ul>\n<h4>Great Prettier support</h4>\n<p>Prettier works better with JSX than with HTM,\nas we'll see below.</p>\n<h4>0 bytes and no runtime overhead</h4>\n<p>JSX is transpiled away.</p>\n<h4>Shorthand syntax for fragments</h4>\n<p>I.e. <code>&lt;&gt;...&lt;/&gt;</code>.</p>\n<h3 id=\"jsx-cons\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#jsx-cons\">JSX cons</a></h3>\n<h4>Needs a transpiler</h4>\n<p>More tooling = more things to setup and maintain.</p>\n<p>Babel can be run in the browser,\nbut that would be slow.</p>\n<h4>New syntax to learn</h4>\n<p>JSX is not regular JavaScript;\nit's a <em>syntax extension</em> to JavaScript.</p>\n<p>Personally I have no problems with the JSX syntax,\nbut I have seen some people struggling with it:\nwhen to use curly braces etc.</p>\n<h2 id=\"htm\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#htm\">HTM</a></h2>\n<p><a href=\"https://github.com/developit/htm\" class=\"link link-external\">HTM (Hyperscript Tagged Markup)</a> is a JS library\nthat provides a <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates\" class=\"link link-external\">tagged template</a>\nfor writing JSX-like markup.</p>\n<p>For example:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> html <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'htm/react'</span>\n\n<span class=\"token keyword\">return</span> html<span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n  &lt;div className=\"foo\">bar&lt;/div>\n</span><span class=\"token template-punctuation string\">`</span></span>\n</span></code></pre>\n<h3 id=\"htm-pros\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#htm-pros\">HTM pros</a></h3>\n<h4>No transpiler needed</h4>\n<p>Implications:</p>\n<ul>\n<li>Can be run in the browser.</li>\n<li>Less tooling to setup and maintain.</li>\n</ul>\n<h4>HTM is tiny and fast</h4>\n<p>Thus it's OK to use HTM in the browser,\nat least during development.\nProbably in production as well\n(just get started and optimize performance later if needed!).</p>\n<h4>0 bytes and no runtime overhead if compiled away</h4>\n<p>For production builds,\nconsider compiling HTM away using <a href=\"https://github.com/developit/htm/tree/master/packages/babel-plugin-htm\" class=\"link link-external\">babel-plugin-htm</a>.</p>\n<h4>No new JavaScript syntax to learn</h4>\n<p>HTM is just standard <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates\" class=\"link link-external\">tagged templates</a>,\ni.e. regular JavaScript\n(as opposed to JSX, which is a syntax extension to JavaScript).</p>\n<p>HTM needs to be used in a certain way,\nso you do need to learn &quot;HTM syntax,&quot;\nbut no <em>new JavaScript syntax</em>.</p>\n<h4>Syntax highlighting support in editors</h4>\n<p><a href=\"https://github.com/developit/htm\" class=\"link link-external\">See HTM's readme for an up-to-date list of editor extensions/plugins</a></p>\n<h4>OK Prettier support</h4>\n<p>Prettier supports HTM, and that's great.\nI wouldn't personally even consider using HTM if Prettier didn't support it.</p>\n<p>But the outcome is not always very pretty,\nas we'll see below.</p>\n<p>I recommend setting\n<a href=\"https://prettier.io/docs/en/options.html#html-whitespace-sensitivity\" class=\"link link-external\">Prettier's <code>htmlWhitespaceSensitivity</code> option</a>\nto <code>'ignore'</code>\nbecause HTM strips (most) extra whitespace away like JSX does.\n(There might be some differences between how HTM and JSX handle whitespace;\npay attention.)</p>\n<h4>Shorthand syntax for components' closing tags</h4>\n<p>E.g. <code>&lt;${Footer}&gt;content&lt;//&gt;</code>.</p>\n<p>Using the shorthand much can make things hard to read,\nbut it might anyway be better to split large components into smaller pieces.</p>\n<h4>Supports HTML-like comments</h4>\n<p>E.g. <code>&lt;!-- Hey there --&gt;</code>.</p>\n<p>HTML-like comments are stripped out,\nwhich is a good or a bad thing depending on the situation.</p>\n<h4>Implicit fragments</h4>\n<p>Returning <code>html`&lt;div /&gt;&lt;div /&gt;`</code> automatically wraps the two <code>div</code>s inside a fragment.\nOr actually an array:\n<a href=\"https://github.com/developit/htm/issues/175\" class=\"link link-external\">HTM issue #175 on GitHub is about implicit fragments</a>.</p>\n<h3 id=\"htm-cons\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#htm-cons\">HTM cons</a></h3>\n<h4>Fails silently on syntax errors</h4>\n<p>This can cause some head scratching.\nRelated issues on GitHub:</p>\n<ul>\n<li><a href=\"https://github.com/developit/htm/issues/56\" class=\"link link-external\">HTM issue #56</a></li>\n<li><a href=\"https://github.com/developit/htm/issues/122\" class=\"link link-external\">HTM issue #122</a></li>\n<li><a href=\"https://github.com/developit/htm/issues/166\" class=\"link link-external\">HTM issue #166</a></li>\n<li><a href=\"https://github.com/developit/htm/issues/168\" class=\"link link-external\">HTM issue #168</a></li>\n<li><a href=\"https://github.com/developit/htm/issues/208\" class=\"link link-external\">HTM issue #208</a></li>\n</ul>\n<h4>No IntelliSense or TypeScript support</h4>\n<p><a href=\"https://github.com/developit/htm/issues/73\" class=\"link link-external\">HTM issue #73 on GitHub is about TypeScript support.</a></p>\n<p>On the other hand,\nif you use TypeScript,\nwhy not just use TSX?\nIt's compiled away.</p>\n<h4>No ESLint plugins</h4>\n<ul>\n<li><a href=\"https://github.com/developit/htm/issues/183\" class=\"link link-external\">HTM issue #183 on GitHub is a feature request for an ESLint plugin</a></li>\n<li><a href=\"https://github.com/developit/htm/issues/198\" class=\"link link-external\">HTM issue #198 on GitHub is about eslint-plugin-jsx-a11y</a></li>\n</ul>\n<h4>Prettier quirk: <code>&lt;pre&gt;</code> tags are wonky</h4>\n<p>As mentioned above,\nHTM strips (most) extra whitespace away like JSX does.</p>\n<p>However,\ninside <code>&lt;pre&gt;</code> tags Prettier treats some non-significant whitespace as significant,\nso the indentation can get really wonky.</p>\n<p>Example (tested with Prettier 2.2.0 and 2.6.2);\nI have borked the indentation quite much for illustrative purposes:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// This code is formatted with Prettier but is very ugly!</span>\n<span class=\"token keyword\">return</span> html<span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n  &lt;pre>\n &lt;code   className=\"foo\"    dangerouslySetInnerHTML=</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>highlightedCode\n    <span class=\"token operator\">?</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">__html</span><span class=\"token operator\">:</span> highlightedCode <span class=\"token punctuation\">}</span>\n    <span class=\"token operator\">:</span> <span class=\"token keyword\">undefined</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n>\n      </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token operator\">!</span>highlightedCode <span class=\"token operator\">||</span> plainCode<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n\n    &lt;/code  >\n     &lt;/pre>\n</span><span class=\"token template-punctuation string\">`</span></span>\n</span></code></pre>\n<p>Workaround: use <code>&lt;${'pre'}&gt;</code> instead!\nThen Prettier won't treat non-significant whitespace as significant.\nExample:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// This code is formatted with Prettier, looks OK</span>\n<span class=\"token keyword\">return</span> html<span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n  &lt;</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token string\">'pre'</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">>\n    &lt;code\n      className=\"foo\"\n      dangerouslySetInnerHTML=</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>highlightedCode\n        <span class=\"token operator\">?</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">__html</span><span class=\"token operator\">:</span> highlightedCode <span class=\"token punctuation\">}</span>\n        <span class=\"token operator\">:</span> <span class=\"token keyword\">undefined</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n    >\n      </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token operator\">!</span>highlightedCode <span class=\"token operator\">||</span> plainCode<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n    &lt;/code>\n  &lt;//>\n</span><span class=\"token template-punctuation string\">`</span></span>\n</span></code></pre>\n<p>JSX for comparison (better formatting in my opinion):</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// This code is formatted with Prettier, looks beautiful!</span>\n<span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>pre</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>code</span>\n      <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>foo<span class=\"token punctuation\">\"</span></span>\n      <span class=\"token attr-name\">dangerouslySetInnerHTML</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>\n        highlightedCode <span class=\"token operator\">?</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">__html</span><span class=\"token operator\">:</span> highlightedCode <span class=\"token punctuation\">}</span> <span class=\"token operator\">:</span> <span class=\"token keyword\">undefined</span>\n      <span class=\"token punctuation\">}</span></span>\n    <span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token punctuation\">{</span><span class=\"token operator\">!</span>highlightedCode <span class=\"token operator\">||</span> plainCode<span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>code</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n  </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>pre</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h4><em>Obsolete:</em> Prettier quirk: consecutive <code>${expressions}</code> can be messy</h4>\n<p><em>Update:</em>\nI noticed that Prettier has this quirk only when formatting JS code blocks in Markdown files,\nnot when formatting JS files.\nMy bad!</p>\n<details>\n<summary>Example of messy consecutive <code>${expressions}</code> (click to toggle)</summary>\n<p>Tested with Prettier 2.2.0 and 2.6.2;\nnotice how the two expressions end/start on the same line\n(but only when formatting a JS code block in a Markdown file!):</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">return</span> html<span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n  &lt;div>\n    </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>foo <span class=\"token operator\">&amp;&amp;</span>\n    html<span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n      &lt;p>Foo...&lt;/p>\n    </span><span class=\"token template-punctuation string\">`</span></span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>bar <span class=\"token operator\">&amp;&amp;</span>\n    html<span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n      &lt;p>Bar...&lt;/p>\n    </span><span class=\"token template-punctuation string\">`</span></span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n  &lt;/div>\n</span><span class=\"token template-punctuation string\">`</span></span>\n</span></code></pre>\n</details>\n<h4>Having to repeat <code>html`...`</code> is tedious</h4>\n<p>E.g. when mapping arrays\nor with conditional rendering.</p>\n<p>I also dislike how Prettier spans the code on many more lines than with JSX.</p>\n<p>Contrived example:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">return</span> html<span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n  &lt;ul>\n    </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>items<span class=\"token punctuation\">.</span>length <span class=\"token operator\">===</span> <span class=\"token number\">0</span> <span class=\"token operator\">&amp;&amp;</span>\n    html<span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n      &lt;li>No items&lt;/li>\n    </span><span class=\"token template-punctuation string\">`</span></span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n    </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>items<span class=\"token punctuation\">.</span>length <span class=\"token operator\">></span> <span class=\"token number\">0</span> <span class=\"token operator\">&amp;&amp;</span>\n    items<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">(</span><span class=\"token parameter\">item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> html<span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n        &lt;li key=</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>item<span class=\"token punctuation\">.</span>id<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">></span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>item<span class=\"token punctuation\">.</span>text<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">&lt;/li>\n      </span><span class=\"token template-punctuation string\">`</span></span>\n    <span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n  &lt;/ul>\n</span><span class=\"token template-punctuation string\">`</span></span>\n</span></code></pre>\n<p>JSX for comparison (much more compact):</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>ul</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n    </span><span class=\"token punctuation\">{</span>items<span class=\"token punctuation\">.</span>length <span class=\"token operator\">===</span> <span class=\"token number\">0</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>li</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">No items</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>li</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n    </span><span class=\"token punctuation\">{</span>items<span class=\"token punctuation\">.</span>length <span class=\"token operator\">></span> <span class=\"token number\">0</span> <span class=\"token operator\">&amp;&amp;</span>\n      items<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>li</span> <span class=\"token attr-name\">key</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>item<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>item<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>li</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n  </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>ul</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h4>HTML entities are escaped</h4>\n<p>For example,\nthe text inside\n<code>html`&lt;span&gt;1&amp;ndash;2&lt;/span&gt;`</code>\ngets rendered as\n<code>1&amp;ndash;2</code>\ninstead of\n<code>1–2</code>.\n(<code>&amp;ndash;</code> = en dash.)</p>\n<p><a href=\"https://github.com/developit/htm/issues/76\" class=\"link link-external\">HTM issue #76 on GitHub is about how HTML entities are handled.</a></p>\n<p>This makes it more difficult to use special characters in components' text contents.\nYou could type them out as-is,\nbut having special characters in source code is bad\nbecause it can be difficult to distinguish between similar characters.\nFor example between\n<code>-</code> (hyphen),\n<code>–</code> (en dash)\nand <code>—</code> (em dash).</p>\n<p>A workaround is to create e.g. <code>char.js</code> and use it like this:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// char.js</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">ndash</span><span class=\"token operator\">:</span> <span class=\"token string\">'–'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">mdash</span><span class=\"token operator\">:</span> <span class=\"token string\">'—'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// Component.js</span>\n<span class=\"token keyword\">import</span> char <span class=\"token keyword\">from</span> <span class=\"token string\">'./char.js'</span>\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> html<span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n  &lt;span>1</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>char<span class=\"token punctuation\">.</span>ndash<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">2&lt;/span>\n</span><span class=\"token template-punctuation string\">`</span></span>\n</span></code></pre>\n<h4>No support for comments inside opening/closing tags</h4>\n<p>HTM supports only HTML-like comments,\nso you can't comment specific props or attributes.</p>\n<p>In JSX you can comment props and attributes:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span>\n  <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>foo<span class=\"token punctuation\">\"</span></span>\n  <span class=\"token attr-name\">disabled</span> <span class=\"token comment\">// Disabled because x</span>\n  <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>bar<span class=\"token punctuation\">\"</span></span>\n  <span class=\"token comment\">// Using `baz` instead of `qux` because y</span>\n  <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>baz<span class=\"token punctuation\">}</span></span>\n<span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n  Click me\n</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<h4>Can't use backticks in comments</h4>\n<p>Or they can be used\nbut have to be escaped.</p>\n<p>Backticks would be useful when referring to variables in comments.\nEscaped backticks and single quotes look silly:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">return</span> html<span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">\n  &lt;div>\n    &lt;!-- 'foo' is something -->\n    </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>foo<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n\n    &lt;!-- But \\`bar\\` is something else -->\n    </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>bar<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">\n  &lt;/div>\n</span><span class=\"token template-punctuation string\">`</span></span>\n</span></code></pre>\n<p>JSX for comparison:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n    </span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* `foo` is something */</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n    </span><span class=\"token punctuation\">{</span>foo<span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n\n    </span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* But `bar` is something else */</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n    </span><span class=\"token punctuation\">{</span>bar<span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n  </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h2 id=\"verdict\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#verdict\">Verdict</a></h2>\n<p>I like the idea of HTM a lot:\nit's just JavaScript tagged templates,\nso there's no need for a transpiler\nand no need to learn new JavaScript syntax.</p>\n<p>HTM can be a good choice for browser environments\n(when you can't or don't want to use a transpiler)\nand for smaller projects.</p>\n<p>The limitations of HTM\n(no IntelliSense, no ESLint plugins, etc.)\ncan however become too limiting in bigger projects.\nOr at least I would imagine so,\nI haven't used HTM in any large projects.</p>\n<p>Here's my non-prescriptive rules for choosing between JSX and HTM:</p>\n<ul>\n<li>Choose HTM if:\n<ol>\n<li>You can't or don't want to use a transpiler.</li>\n<li>You can live without IntelliSense and ESLint plugins.</li>\n<li>Optional: you struggle with JSX syntax.\n(In comparison, HTM is just JavaScript.)</li>\n</ol>\n</li>\n<li>Otherwise choose JSX.\nIt requires a transpiler,\nbut JSX is really robust.</li>\n</ul>\n<p>But hey,\nwhat about\n<a href=\"https://mtsknn.fi/blog/hyperscript-hyperior-alternative-to-jsx/\" class=\"link\">another JSX alternative, hyperscript</a>...?\nI'll leave the decision to you. 😁</p>\n","summary":"HTM provides a transpiler-free alternative to JSX via tagged templates.\nHTM has some limitations,\nbut can be used in browsers\nand can be good for smaller projects.","date_published":"2021-08-23T12:00:00+03:00","date_modified":"2022-04-10T12:00:00+03:00","tags":["JavaScript","React"]},{"id":"https://mtsknn.fi/blog/fizzbuzz-in-javascript-with-infinite-arrays-and-multimapping/","url":"https://mtsknn.fi/blog/fizzbuzz-in-javascript-with-infinite-arrays-and-multimapping/","title":"FizzBuzz in JavaScript with infinite arrays and multimapping","content_html":"<p>Fun way to solve the simple puzzle.\nInfinitely repeating arrays\nand mapping over multiple arrays\nalmost makes the remainder/modulo operator (<code>%</code>) unnecessary.</p>\n<h2 id=\"tldr\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#tldr\">TL;DR</a></h2>\n<p><a href=\"#finished-result-playground\" class=\"link link-anchor\">Jump to the <em>Finished result + playground</em> section →</a></p>\n<h2 id=\"rules-of-fizzbuzz\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#rules-of-fizzbuzz\">Rules of FizzBuzz</a></h2>\n<p>A FizzBuzz sequence consists of numbers and strings where:</p>\n<ul>\n<li>every 3rd item is &quot;Fizz&quot;</li>\n<li>every 5th item is &quot;Buzz&quot;</li>\n<li>every 15th item is &quot;FizzBuzz&quot;</li>\n<li>and other items are numbers.</li>\n</ul>\n<p>So the first 15 items are:</p>\n<ol>\n<li>1</li>\n<li>2</li>\n<li>Fizz</li>\n<li>4</li>\n<li>Buzz</li>\n<li>Fizz</li>\n<li>7</li>\n<li>8</li>\n<li>Fizz</li>\n<li>Buzz</li>\n<li>11</li>\n<li>Fizz</li>\n<li>13</li>\n<li>14</li>\n<li>FizzBuzz</li>\n</ol>\n<p>After that,\nthe cycle repeats.\n(The 16th item is 16 instead of 1,\nand so on.)</p>\n<h2 id=\"the-challenge\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-challenge\">The challenge</a></h2>\n<p>Here's a typical solution, though without a <code>for</code> loop:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">15</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">_<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> n <span class=\"token operator\">=</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">%</span> <span class=\"token number\">15</span> <span class=\"token operator\">===</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token string\">'FizzBuzz'</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">%</span> <span class=\"token number\">3</span> <span class=\"token operator\">===</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token string\">'Fizz'</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">%</span> <span class=\"token number\">5</span> <span class=\"token operator\">===</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token string\">'Buzz'</span>\n  <span class=\"token keyword\">return</span> n\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// [</span>\n<span class=\"token comment\">//        1,      2, 'Fizz',      4,     'Buzz',</span>\n<span class=\"token comment\">//   'Fizz',      7,      8, 'Fizz',     'Buzz',</span>\n<span class=\"token comment\">//       11, 'Fizz',     13,     14, 'FizzBuzz',</span>\n<span class=\"token comment\">// ]</span>\n</span></code></pre>\n<p>It works a-okay,\nbut <strong>let's solve FizzBuzz without using the <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Remainder\" class=\"link link-external\">remainder operator</a> in the usual way</strong>,\ni.e. without these:</p>\n<ul>\n<li><code>if (n % 15 === 0)</code></li>\n<li><code>if (n % 3 === 0)</code></li>\n<li><code>if (n % 5 === 0)</code></li>\n</ul>\n<p>Before that,\nwe need to acquire two new skills:\ncreating infinite arrays\nand mapping over multiple arrays.</p>\n<h2 id=\"infinite-arrays\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#infinite-arrays\">Infinite arrays</a></h2>\n<p>How arrays normally work (d'oh):</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> array <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'A'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'B'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'C'</span><span class=\"token punctuation\">]</span>\n\narray<span class=\"token punctuation\">.</span>length <span class=\"token comment\">//=> 3</span>\n\narray<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">//=> 'A'</span>\narray<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">//=> 'B'</span>\narray<span class=\"token punctuation\">[</span><span class=\"token number\">2</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">//=> 'C'</span>\narray<span class=\"token punctuation\">[</span><span class=\"token number\">3</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">//=> undefined</span>\narray<span class=\"token punctuation\">[</span><span class=\"token number\">4</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">//=> undefined</span>\n<span class=\"token comment\">// ...</span>\n</span></code></pre>\n<p>What we want:\nwhen accessing an out-of-bounds numeric property of the <code>array</code> object,\ncycle back to the beginning of the array.</p>\n<p>That can be achieved by creating\na <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy\" class=\"link link-external\"><code>Proxy</code></a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> proxyHandler <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">get</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">,</span> prop<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// Accessing `target.length`</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>prop <span class=\"token operator\">===</span> <span class=\"token string\">'length'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token number\">Infinity</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// Accessing a numeric property of `target`, e.g. `target[5]`</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">typeof</span> prop <span class=\"token operator\">===</span> <span class=\"token string\">'string'</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">^\\d+$</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">.</span><span class=\"token function\">test</span><span class=\"token punctuation\">(</span>prop<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> target<span class=\"token punctuation\">[</span>prop <span class=\"token operator\">%</span> target<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">// Accessing whatever else, e.g. `target.foo`</span>\n    <span class=\"token keyword\">return</span> target<span class=\"token punctuation\">[</span>prop<span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">cycle</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">array</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Proxy</span><span class=\"token punctuation\">(</span>array<span class=\"token punctuation\">,</span> proxyHandler<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>I named the function <code>cycle</code>\nbecause I was inspired by\n<a href=\"https://clojuredocs.org/clojure.core/cycle\" class=\"link link-external\">Clojure's <code>cycle</code> function</a>.</p>\n<p>Usage:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> infiniteArray <span class=\"token operator\">=</span> <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'A'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'B'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'C'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\ninfiniteArray<span class=\"token punctuation\">.</span>length <span class=\"token comment\">//=> Infinity</span>\n\ninfiniteArray<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">//=> 'A'</span>\ninfiniteArray<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">//=> 'B'</span>\ninfiniteArray<span class=\"token punctuation\">[</span><span class=\"token number\">2</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">//=> 'C'</span>\ninfiniteArray<span class=\"token punctuation\">[</span><span class=\"token number\">3</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">//=> 'A'</span>\ninfiniteArray<span class=\"token punctuation\">[</span><span class=\"token number\">4</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">//=> 'B'</span>\ninfiniteArray<span class=\"token punctuation\">[</span><span class=\"token number\">5</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">//=> 'C'</span>\ninfiniteArray<span class=\"token punctuation\">[</span><span class=\"token number\">6</span><span class=\"token punctuation\">]</span> <span class=\"token comment\">//=> 'A'</span>\n<span class=\"token comment\">// ...</span>\n</span></code></pre>\n<h3 id=\"notes-about-the-typeof-prop-check\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#notes-about-the-typeof-prop-check\">Notes about the <code>typeof prop</code> check</a></h3>\n<h4>Why is the <code>typeof prop === 'string'</code> check in the proxy handler's <code>get</code> method needed?</h4>\n<p>Otherwise the regex test would throw an error\nif <code>prop</code> was a <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol\" class=\"link link-external\">Symbol</a>.</p>\n<p>For example,\nif <code>prop</code> was\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator\" class=\"link link-external\"><code>Symbol.iterator</code></a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">;</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">^\\d+$</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">.</span><span class=\"token function\">test</span><span class=\"token punctuation\">(</span>Symbol<span class=\"token punctuation\">.</span>iterator<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> TypeError: Cannot convert a Symbol value to a string</span>\n</span></code></pre>\n<p>The error is thrown\nbecause <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test\" class=\"link link-external\"><code>RegExp.prototype.test</code></a>\nexpects a string argument,\nand Symbols can't be implicitly converted to strings.</p>\n<p><code>Symbol.iterator</code> is used by <code>for...of</code> loops.\nSo,\nusing <code>infiniteArray</code> in a <code>for...of</code> loop\nwould thus throw an error\nwithout the <code>typeof</code> check.</p>\n<p>Using any other Symbol property key would throw as well\nwithout the <code>typeof</code> check.</p>\n<h4>Why check that <code>typeof prop</code> is <code>'string'</code> instead of <code>'number'</code>?</h4>\n<p>E.g. in <code>infiniteArray[5]</code> the property key is a number, right?\nWell yes, but actually no.</p>\n<p>Numeric property keys are converted to strings.\nFrom <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects\" class=\"link link-external\"><em>Working with objects</em> on MDN</a>:</p>\n<blockquote>\n<p>All keys in the square bracket notation are converted to string\nunless they're Symbols,\nsince JavaScript object property names (keys)\ncan only be strings or Symbols.</p>\n</blockquote>\n<h4>An alternative to the <code>typeof prop</code> check</h4>\n<p>I said above that Symbols can't be <em>implicitly</em> converted to strings.\nBut a Symbol can be converted <em>explicitly</em> to a string by calling its <code>toString()</code> method.\nDoing so would make the <code>typeof prop</code> check unnecessary:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">typeof</span> prop <span class=\"token operator\">===</span> <span class=\"token string\">'string'</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">^\\d+$</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">.</span><span class=\"token function\">test</span><span class=\"token punctuation\">(</span>prop<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">^\\d+$</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">.</span><span class=\"token function\">test</span><span class=\"token punctuation\">(</span>prop<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>  <span class=\"token keyword\">return</span> target<span class=\"token punctuation\">[</span>prop <span class=\"token operator\">%</span> target<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">]</span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token punctuation\">}</span>\n</span></span></code></pre>\n<p>Neither option is very clear without an explanation.\nI might prefer the <code>toString()</code> alternative.</p>\n<h3 id=\"shortcomings\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#shortcomings\">Shortcomings</a></h3>\n<p>The <code>cycle</code> function is imperfect.\nFor example,\nslicing doesn't work:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> infiniteArray <span class=\"token operator\">=</span> <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token string\">'A'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'B'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'C'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// Current behavior:</span>\ninfiniteArray<span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">7</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['A', 'B', 'C', empty × 4]</span>\n\n<span class=\"token comment\">// Ideal behavior:</span>\ninfiniteArray<span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">7</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['A', 'B', 'C', 'A', 'B', 'C', 'A']</span>\n</span></code></pre>\n<p>Supporting all array methods would be interesting.\nI'll leave it to you as an exercise.</p>\n<p>The current imperfect implementation is enough for solving FizzBuzz,\nso let's proceed.</p>\n<h2 id=\"multimapping\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#multimapping\">Multimapping</a></h2>\n<p><a href=\"https://clojuredocs.org/clojure.core/map\" class=\"link link-external\">Clojure's <code>map</code> function</a>\nis similar to\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map\" class=\"link link-external\">JavaScript arrays' <code>map</code> method</a>,\nexcept in Clojure you can map over multiple arrays at once:</p>\n<blockquote>\n<p><code>map</code> returns a lazy sequence consisting of the result of\napplying [the function] <code>f</code> to the set of first items of each <code>coll</code>,\nfollowed by applying <code>f</code> to the set of second items in each <code>coll</code>,\nuntil any one of the <code>coll</code>s is exhausted.\nAny remaining items in other <code>coll</code>s are ignored.</p>\n</blockquote>\n<p>A <code>multimap</code> function can be implemented in a few lines of JavaScript\n(I'm ignoring the part about returning a lazy sequence):</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">multimap</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">fn<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>arrays</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> arrayLengths <span class=\"token operator\">=</span> arrays<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">array</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> array<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> smallestArrayLength <span class=\"token operator\">=</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">min</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span>arrayLengths<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> smallestArrayLength <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">_<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> items <span class=\"token operator\">=</span> arrays<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">array</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> array<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span>items<span class=\"token punctuation\">,</span> i<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Usage:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Same lenghts -> loop over 4 times</span>\n<span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">,</span> args<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> i\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token string\">'A'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'B'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'C'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'D'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token string\">'W'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'X'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Y'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Z'</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// Console loggings:</span>\n<span class=\"token comment\">//   0, ['A', 'W']</span>\n<span class=\"token comment\">//   1, ['B', 'X']</span>\n<span class=\"token comment\">//   2, ['C', 'Y']</span>\n<span class=\"token comment\">//   3, ['D', 'Z']</span>\n<span class=\"token comment\">// Return value:</span>\n<span class=\"token comment\">//   [0, 1, 2, 3]</span>\n\n<span class=\"token comment\">// Different lengths -> loop over 3 times (the length of the shortest array)</span>\n<span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">,</span> args<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> args<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token string\">'♠'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'♡'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'♢'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'♣'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token string\">'A'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'B'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'C'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'D'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'E'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token string\">'♪'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'♫'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'☻'</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// Console loggings:</span>\n<span class=\"token comment\">//   0, ['♠', 'A', '♪']</span>\n<span class=\"token comment\">//   1, ['♡', 'B', '♫']</span>\n<span class=\"token comment\">//   2, ['♢', 'C', '☻']</span>\n<span class=\"token comment\">// Return value:</span>\n<span class=\"token comment\">//   ['♠', 'B', '☻']</span>\n</span></code></pre>\n<h2 id=\"putting-the-pieces-together-solving-fizzbuzz\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#putting-the-pieces-together-solving-fizzbuzz\">Putting the pieces together: solving FizzBuzz</a></h2>\n<p>With our two new skills –\ncreating (imperfect) infinite arrays\nand mapping over multiple arrays –\nwe are ready to tackle the fearsomely challenging puzzle of FizzBuzz.</p>\n<h3 id=\"step-1-rules-of-fizzbuzz-as-arrays\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-1-rules-of-fizzbuzz-as-arrays\">Step 1: rules of FizzBuzz as arrays</a></h3>\n<p>Let's translate the <a href=\"#rules-of-fizzbuzz\" class=\"link link-anchor\">rules of FizzBuzz</a> to arrays (if that makes sense).\nLet's also focus only on the first ten items of the FizzBuzz sequence for now.</p>\n<ul>\n<li>\n<p><em>&quot;Every 3rd item is 'Fizz'&quot;</em> means that\nevery 3rd item of an array is <code>'Fizz'</code>.\nOther items are irrelevant,\nso they can be nulls:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">]</span>\n</span></code></pre>\n</li>\n<li>\n<p><em>&quot;Every 5th item is 'Buzz'&quot;</em> works similarly:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">]</span>\n</span></code></pre>\n</li>\n<li>\n<p><em>&quot;Every 15th item is 'FizzBuzz'&quot;</em> can be ignored for now\nbecause the rule doesn't apply to a sequence of just ten items.\nBut it would work similarly to the two previous rules/arrays.</p>\n</li>\n<li>\n<p><em>&quot;Other items are numbers&quot;</em> is simply an array of numbers starting from 1:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token number\">4</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">6</span><span class=\"token punctuation\">,</span> <span class=\"token number\">7</span><span class=\"token punctuation\">,</span> <span class=\"token number\">8</span><span class=\"token punctuation\">,</span> <span class=\"token number\">9</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">]</span>\n</span></code></pre>\n</li>\n</ul>\n<p>Let's see how they look when put into the <code>multimap</code> function:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>args<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span>   <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>    <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>      <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>    <span class=\"token number\">4</span><span class=\"token punctuation\">,</span>      <span class=\"token number\">5</span><span class=\"token punctuation\">,</span>      <span class=\"token number\">6</span><span class=\"token punctuation\">,</span>    <span class=\"token number\">7</span><span class=\"token punctuation\">,</span>    <span class=\"token number\">8</span><span class=\"token punctuation\">,</span>      <span class=\"token number\">9</span><span class=\"token punctuation\">,</span>     <span class=\"token number\">10</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// Console loggings:</span>\n<span class=\"token comment\">//   [null,     null,  1]</span>\n<span class=\"token comment\">//   [null,     null,  2]</span>\n<span class=\"token comment\">//   ['Fizz',   null,  3]</span>\n<span class=\"token comment\">//   [null,     null,  4]</span>\n<span class=\"token comment\">//   [null,   'Buzz',  5]</span>\n<span class=\"token comment\">//   ['Fizz',   null,  6]</span>\n<span class=\"token comment\">//   [null,     null,  7]</span>\n<span class=\"token comment\">//   [null,     null,  8]</span>\n<span class=\"token comment\">//   ['Fizz',   null,  9]</span>\n<span class=\"token comment\">//   [null,   'Buzz', 10]</span>\n<span class=\"token comment\">// Return value:</span>\n<span class=\"token comment\">//   [undefined × 10]</span>\n</span></code></pre>\n<p>Hmm, what if we take the first non-null item from each set of items?</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> args<span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">x</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> x <span class=\"token operator\">!==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span>   <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>    <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>      <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>    <span class=\"token number\">4</span><span class=\"token punctuation\">,</span>      <span class=\"token number\">5</span><span class=\"token punctuation\">,</span>      <span class=\"token number\">6</span><span class=\"token punctuation\">,</span>    <span class=\"token number\">7</span><span class=\"token punctuation\">,</span>    <span class=\"token number\">8</span><span class=\"token punctuation\">,</span>      <span class=\"token number\">9</span><span class=\"token punctuation\">,</span>     <span class=\"token number\">10</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [1,    2, 'Fizz',    4, 'Buzz', 'Fizz',    7,    8, 'Fizz', 'Buzz']</span>\n</span></code></pre>\n<p>Hey, FizzBuzz! 😎\n(Every 15th item will be handled soon.)</p>\n<h3 id=\"step-2-truthy-vs-falsy\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-2-truthy-vs-falsy\">Step 2: truthy vs falsy</a></h3>\n<p>The arrays contain only strings, numbers and nulls.\nStrings and numbers are <a href=\"https://developer.mozilla.org/en-US/docs/Glossary/Truthy\" class=\"link link-external\">truthy values</a>,\nand nulls are <a href=\"https://developer.mozilla.org/en-US/docs/Glossary/Falsy\" class=\"link link-external\">falsy values</a>.</p>\n<p>Instead of taking the first non-null item,\nthe first truthy item can be taken,\nand the result will be the same.\nThis simplifies the callback function:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> args<span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>   <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span>   <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>    <span class=\"token number\">2</span><span class=\"token punctuation\">,</span>      <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>    <span class=\"token number\">4</span><span class=\"token punctuation\">,</span>      <span class=\"token number\">5</span><span class=\"token punctuation\">,</span>      <span class=\"token number\">6</span><span class=\"token punctuation\">,</span>    <span class=\"token number\">7</span><span class=\"token punctuation\">,</span>    <span class=\"token number\">8</span><span class=\"token punctuation\">,</span>      <span class=\"token number\">9</span><span class=\"token punctuation\">,</span>     <span class=\"token number\">10</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [1,    2, 'Fizz',    4, 'Buzz', 'Fizz',    7,    8, 'Fizz', 'Buzz']</span>\n</span></code></pre>\n<h3 id=\"step-3-cycling\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-3-cycling\">Step 3: cycling 🚴‍♂️</a></h3>\n<p>The first two arrays are repeating,\nso they can be simplified with the new <code>cycle</code> function:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> args<span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token number\">4</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">6</span><span class=\"token punctuation\">,</span> <span class=\"token number\">7</span><span class=\"token punctuation\">,</span> <span class=\"token number\">8</span><span class=\"token punctuation\">,</span> <span class=\"token number\">9</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz']</span>\n</span></code></pre>\n<h3 id=\"step-4-range-function\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-4-range-function\">Step 4: <code>range</code> function</a></h3>\n<p>The third array is lame\nbecause it's a manually-created range of numbers.\nLet's create a <code>range</code> function:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">range</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">from<span class=\"token punctuation\">,</span> to</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> to <span class=\"token operator\">-</span> from <span class=\"token operator\">+</span> <span class=\"token number\">1</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">_<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> i <span class=\"token operator\">+</span> from<span class=\"token punctuation\">)</span>\n\n<span class=\"token function\">range</span><span class=\"token punctuation\">(</span><span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">8</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [5, 6, 7, 8]</span>\n</span></code></pre>\n<p>Usage:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> args<span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">range</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz']</span>\n</span></code></pre>\n<h3 id=\"step-5-index-argument-and-filler-arrays\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-5-index-argument-and-filler-arrays\">Step 5: <code>index</code> argument and filler arrays</a></h3>\n<p>The <code>range</code> function is not actually even needed.</p>\n<p>The callback function passed to the <code>multimap</code> function\naccepts <code>index</code> as its second argument.\nJust like the callback function passed to\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map\" class=\"link link-external\"><code>Array.prototype.map</code></a>.</p>\n<p>By using the <code>index</code> argument,\nthe third array (range of numbers) is not needed,\nand thus the <code>range</code> function is not needed.</p>\n<p>But the third array can't just be omitted\nas all of the remaining arrays would be infinite:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> args<span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> RangeError: Invalid array length</span>\n</span></code></pre>\n<p>Remember that the <code>multimap</code> function continues going over the arrays\nuntil any of them is exhausted.\nIn other words,\n<code>multimap</code> goes over the arrays <em>n</em> times\nwhere <em>n</em> is the length of the shortest array.</p>\n<p>Thus, <code>multimap</code> can be provided a &quot;filler&quot; array with a certain length.\nBecause the other arrays are infinite,\nthe filler array's length determines how many times <code>multimap</code> goes over the arrays.</p>\n<p>We are not interested in the filler array's values,\nso an array with ten empty slots is fine:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> args<span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">10</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// [empty × 10]</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz']</span>\n</span></code></pre>\n<p>The <code>range</code> function might be clearer,\nbut I'm going to continue using the filler array,\nbecause filler arrays will be useful in the next steps.</p>\n<h3 id=\"step-6-left-padded-arrays\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-6-left-padded-arrays\">Step 6: left-padded arrays</a></h3>\n<p>The first two arrays are mostly fillers too.\nA more concise way of creating them\nis to create filler arrays\nand concatenating them with the last non-filler value:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Before:</span>\n<span class=\"token punctuation\">;</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">;</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">]</span>\n\n<span class=\"token comment\">// After:</span>\n<span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// [empty × 2, 'Fizz']</span>\n<span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">4</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// [empty × 4, 'Buzz']</span>\n</span></code></pre>\n<p>Kinda like left-padding arrays...\nSomeone quickly publish <code>left-pad-array</code> on npm!</p>\n<p>Usage:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> args<span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">4</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">10</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [1, 2, 'Fizz', 4, 'Buzz', 'Fizz', 7, 8, 'Fizz', 'Buzz']</span>\n</span></code></pre>\n<h3 id=\"step-7-the-remaining-rule\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-7-the-remaining-rule\">Step 7: the remaining rule</a></h3>\n<p>The code works so far with Fizzes and Buzzes,\nbut not with FizzBuzzes.\nEvery 15th item is &quot;Fizz&quot; instead of &quot;FizzBuzz&quot;:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> args<span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">4</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">15</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [1,</span>\n<span class=\"token comment\">//    2,</span>\n<span class=\"token comment\">//    'Fizz',</span>\n<span class=\"token comment\">//    4,</span>\n<span class=\"token comment\">//    'Buzz',</span>\n<span class=\"token comment\">//    'Fizz',</span>\n<span class=\"token comment\">//    7,</span>\n<span class=\"token comment\">//    8,</span>\n<span class=\"token comment\">//    'Fizz',</span>\n<span class=\"token comment\">//    'Buzz',</span>\n<span class=\"token comment\">//    11,</span>\n<span class=\"token comment\">//    'Fizz',</span>\n<span class=\"token comment\">//    13,</span>\n<span class=\"token comment\">//    14,</span>\n<span class=\"token comment\">//    'Fizz', // :( Should be 'FizzBuzz'</span>\n<span class=\"token comment\">//   ]</span>\n</span></code></pre>\n<p>An easy fix is to use another array\nbased on the remaining rule (<em>&quot;every 15th item is 'FizzBuzz'&quot;</em>):</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> args<span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">14</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'FizzBuzz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">4</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">15</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [1,</span>\n<span class=\"token comment\">//    2,</span>\n<span class=\"token comment\">//    'Fizz',</span>\n<span class=\"token comment\">//    4,</span>\n<span class=\"token comment\">//    'Buzz',</span>\n<span class=\"token comment\">//    'Fizz',</span>\n<span class=\"token comment\">//    7,</span>\n<span class=\"token comment\">//    8,</span>\n<span class=\"token comment\">//    'Fizz',</span>\n<span class=\"token comment\">//    'Buzz',</span>\n<span class=\"token comment\">//    11,</span>\n<span class=\"token comment\">//    'Fizz',</span>\n<span class=\"token comment\">//    13,</span>\n<span class=\"token comment\">//    14,</span>\n<span class=\"token comment\">//    'FizzBuzz', // :) Correct</span>\n<span class=\"token comment\">//   ]</span>\n</span></code></pre>\n<p>But the new array is not actually necessary\nbecause Fizz + Buzz = FizzBuzz:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> args<span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token string\">''</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">4</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">15</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [1,</span>\n<span class=\"token comment\">//    2,</span>\n<span class=\"token comment\">//    'Fizz',</span>\n<span class=\"token comment\">//    4,</span>\n<span class=\"token comment\">//    'Buzz',</span>\n<span class=\"token comment\">//    'Fizz',</span>\n<span class=\"token comment\">//    7,</span>\n<span class=\"token comment\">//    8,</span>\n<span class=\"token comment\">//    'Fizz',</span>\n<span class=\"token comment\">//    'Buzz',</span>\n<span class=\"token comment\">//    11,</span>\n<span class=\"token comment\">//    'Fizz',</span>\n<span class=\"token comment\">//    13,</span>\n<span class=\"token comment\">//    14,</span>\n<span class=\"token comment\">//    'FizzBuzz', // Still correct</span>\n<span class=\"token comment\">//   ]</span>\n</span></code></pre>\n<h2 id=\"finished-result-playground\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#finished-result-playground\">Finished result + playground</a></h2>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">cycle</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">array</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  <span class=\"token keyword\">new</span> <span class=\"token class-name\">Proxy</span><span class=\"token punctuation\">(</span>array<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">get</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">,</span> prop<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>prop <span class=\"token operator\">===</span> <span class=\"token string\">'length'</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token number\">Infinity</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">^\\d+$</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">.</span><span class=\"token function\">test</span><span class=\"token punctuation\">(</span>prop<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> target<span class=\"token punctuation\">[</span>prop <span class=\"token operator\">%</span> target<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">]</span>\n      <span class=\"token keyword\">return</span> target<span class=\"token punctuation\">[</span>prop<span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">multimap</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">fn<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>arrays</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> arrayLengths <span class=\"token operator\">=</span> arrays<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">array</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> array<span class=\"token punctuation\">.</span>length<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> smallestArrayLength <span class=\"token operator\">=</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">min</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span>arrayLengths<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> smallestArrayLength <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">_<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> items <span class=\"token operator\">=</span> arrays<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">array</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> array<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span>items<span class=\"token punctuation\">,</span> i<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">range</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">from<span class=\"token punctuation\">,</span> to</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> to <span class=\"token operator\">-</span> from <span class=\"token operator\">+</span> <span class=\"token number\">1</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">_<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> i <span class=\"token operator\">+</span> from<span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// Variation 1:</span>\n<span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> args<span class=\"token punctuation\">.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">14</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'FizzBuzz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">4</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">range</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">15</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// Variation 2:</span>\n<span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\">args<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> args<span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token string\">''</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">4</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">15</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>There are a lot more variations to discover.\nFor example, these two are nice:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">[</span>fizz<span class=\"token punctuation\">,</span> buzz<span class=\"token punctuation\">,</span> number<span class=\"token punctuation\">]</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">[</span>fizz<span class=\"token punctuation\">,</span> buzz<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">join</span><span class=\"token punctuation\">(</span><span class=\"token string\">''</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> number<span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">range</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">15</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n\n<span class=\"token function\">multimap</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">[</span>word<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> word <span class=\"token operator\">||</span> i <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">cycle</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Buzz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Fizz'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'FizzBuzz'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">Array</span><span class=\"token punctuation\">(</span><span class=\"token number\">15</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p><a href=\"https://flems.io/#0=N4IgtglgJlA2CmIBcBGADGgNCAZhBAzsgNqgB2AhmIkiAHQBWR2AxgPZkAu8XyIA9PwAEABVgUAngHMATmwCuZKEJxsZQgDogAYhABeegELyDQiGSEApCgDcKAZRYyIAB05CA7hE4ALM2Twyb3ghChkZSQJQpSEweVhOCDAKFxdzKS0hACMJIQBZCkSKKIBpczJFHjoNMkEhe3gQn05OFwIkQTBOAgBrMjI6PH4s2DYpfjwDLJM9AFpzWYZbYqdXTlmvXwV1mXhk8yh4GVmwNih4tlm2FyPCtX4amvYyAncWCRYEIQBeIQAKMIRCQASh+AD4hGR4B5RHIAB4SAHhSSYITAGpCIRSeCcP6cMLYziolxyFyg9EWTFmHD-EnXH7fX4AcgQZCkviZoN2nHkMgsAEkAuZvBIMVSIDS-vwAHoaDRQADUABJ+HRuK8-nSXGq2PZOM42X9gcahNzeRZ8TJCcQtUIAKRCS2Euis9k+AC6YsxZr5joJOJtpM9lIAvpgaiHgY8yM9XrF4olki4fv8cGRUXRM4DIqDvhCKZjY+5sxIADI8N1RX4lgh0JN-JFA3MQksuiu+KOUotCAjJWCETgAQWRZfbfl+BV8dfMf0zdBL5bZvgInbFPosw6BgzkYD+wCErt8SB7fYHm8ki7dQkjdZSDYA+qiIM20V6hN3gmAq6ER7X643JBfEtiAgd1OypU0cXNFQyD+T8CCfcDr07ENo27CI2RCX4-hwHdUU4NhmzFc8JG3Nhd33Q8fGPAihFmFQdyEBUhBQa9UT+R8zBfCAmIY8jV1qYQ9XgZMUBqBB3F2AgExTOIEiSO8xSRKQV3BH8VMGA4GzhF84SEABCRlIXiWBgXDSliAqftUSs2BUSZXQDCZGyTNRTFbPsxy9Gc4zrN8uyhAc-RvLc-z3XMzFLNc-zQo8mLAuMJzYuiuK4vc6KmUS7zws0CyqRQUKhAAJkKzEAGZCoAFlKoQAFYaoANkKgB2QqAA4aoATlK9Bg07OpBSEbQEC-VEAAMXCNMaUzG2M2AQF0xim-5pncCblooWACDYSCeT5KJfBCPAZDjAlUXYebO0mplhNEpAfKkhMBLqW7ipqR6ElkhMFMmpSCVUvN1NrQIoD+Qw2Hm+AKDIMyxSivy4qCpL4vSvykZC+LEa8nzUdgcK4dS6LcZctGspxzGUqJ+LMpmJkcrh-LCpKiDyqqmr6pZoQmog1qII6znupZ3qaiuv4bu4ZMivu1EPs4Z6hIloQyve+BpM+345MTRTKWUgGWytYGtPByHodhrsPgQP54YCrHgrps3Cwt+ArcJhHKdJ2mwIioRiAK4rUQqoRqrq1Fud5-nBeFmGamu16yulyC1bl6MXsVyqVaTr75PrP6DaAg3NKUMGIYQU3vfeT5netkmAvR+3y6dl33Zt5v7LJr212h7E-j99BO1F8WRKDhPZfl+pFdqjOZI176c51-7ELU-7C9B43S5hoQAB9N7MXiCrFCvLer6nsY783K6bt2r5bj2nLPzESJ7tB+5jsXXtqkfVaelOFaHhqp-VvGbO2tMS60XoDZeINi4mw3tvXezF97n0to-IqwI6DPBYIUMW2NjQNwvo-SqaCMFYJpk5XBxERxPxfmQWOisGqfyTmPV6vM7DOEKBADgLEAHuBnsA3688DbgP1hpKBa8oawJ3jxBBeDkGUJQIQ9BHBMG4nRmTchSDnYoKIUokhOCHbvkbgQ7RMYSFqP0Y-Mqz8Rav0HsmZqDDv5kBqKnIeLCwgQHYZwoq3Cs5a34aAheXEl4FwYGwGcTJORb0kXvGRmjKGoMUSYlRejYl-CMYk5RYszHewsVY6ONC36K3sQ9L+CQx6DjIGwQ66hWEeMSBwHxvC-F-CUsQSYehUTTAMC5MAWQjhgTUm04KnSZjukYGE2CETQRwIqL0o4qTj62zvvow+VdXY3w2W3T2+iMLdz9pY6h11wYVHaIFGWpTk5OMEkICpygJA4miFUnwRxGlAOaa0jwagoA5WfGpT5MhlBwKkSxBZ6zPJ2xrls5GddIUnwhQlWmsKYUUzRl5du5jKEHOsQUzKHB5CnJKYwkArByJpAQDIZAtkQzhRAJQagfB2ldL0EIFw4hpByEUFAEAIYgA\" class=\"link link-external\">Play around with the code on flems.io</a></p>\n<p>What kind of cool variations can you come up with?\nSend me an email! 😎\n(Address on the <a href=\"/\" class=\"link\">home page</a>.)</p>\n<h2 id=\"acknowledgements\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#acknowledgements\">Acknowledgements</a></h2>\n<p>The approach and code in this blog post\nwere like 90% inspired by\nthe <a href=\"https://www.youtube.com/watch?v=hSqpJpowazg\" class=\"link link-external\">YouTube video <em>FizzBuzz in Clojure with &amp; without modulus/remainder/rest</em> by Fred Overflow</a>\n(cool name!).</p>\n<p>I wanted to call this blog post\n&quot;FizzBuzz in JS without the remainder operator,&quot;\nbut I couldn't\nas I had to use the operator in the <code>cycle</code> function.\n<span class=\"whitespace-no-wrap\">¯\\(ツ)/¯</span></p>\n","summary":"Fun way to solve the simple puzzle.\nInfinitely repeating arrays\nand mapping over multiple arrays\nalmost makes the remainder/modulo operator (`%`) unnecessary.","date_published":"2021-08-14T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/memoized-fibonacci-function-in-1-line-of-javascript/","url":"https://mtsknn.fi/blog/memoized-fibonacci-function-in-1-line-of-javascript/","title":"Memoized Fibonacci function in 1 line of JavaScript","content_html":"<p>Using recursion,\nlogical OR assignment operator (<code>||=</code>),\nBigInts\nand more!</p>\n<h2 id=\"whats-fibonacci\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#whats-fibonacci\">What's Fibonacci?</a></h2>\n<p>From <a href=\"https://en.wikipedia.org/w/index.php?title=Fibonacci_number&amp;oldid=1035985207\" class=\"link link-external\"><em>Fibonacci number</em> on Wikipedia</a>:</p>\n<blockquote>\n<p>In mathematics,\nthe <strong>Fibonacci numbers</strong>,\ncommonly denoted <em>F<sub>n</sub></em>,\nform a sequence,\ncalled the <strong>Fibonacci sequence</strong>,\nsuch that each number is the sum of the two preceding ones,\nstarting from 0 and 1.\nThat is,</p>\n<blockquote>\n<p><em>F</em><sub>0</sub> = 0,<br>\n<em>F</em><sub>1</sub> = 1,</p>\n</blockquote>\n<p>and</p>\n<blockquote>\n<p><em>F<sub>n</sub></em> = <em>F<sub>n-1</sub></em> + <em>F<sub>n-2</sub></em></p>\n</blockquote>\n<p>for <em>n &gt; 1</em>.</p>\n<p>The sequence starts:</p>\n<p>0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …</p>\n</blockquote>\n<h2 id=\"whats-memoization\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#whats-memoization\">What's memoization?</a></h2>\n<p>From <a href=\"https://en.wikipedia.org/w/index.php?title=Memoization&amp;oldid=1034309521\" class=\"link link-external\"><em>Memoization</em> on Wikipedia</a>:</p>\n<blockquote>\n<p>In computing,\n<strong>memoization</strong> or <strong>memoisation</strong> is an optimization technique\nused primarily to speed up computer programs\nby storing the results of expensive function calls\nand returning the cached result\nwhen the same inputs occur again.</p>\n</blockquote>\n<h2 id=\"step-1-recursive-function-without-memoization\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-1-recursive-function-without-memoization\">Step 1: Recursive function without memoization</a></h2>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> n\n  <span class=\"token keyword\">return</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Negative inputs are returned as-is,\nand non-number inputs may break the function.\nThis is addressed in the <a href=\"#bonus-3-input-validation-and-typescript-types\" class=\"link link-anchor\">3rd bonus section</a>;\nfor now,\nlet's just live with it.</p>\n<p>Usage:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 0</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 1</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 1</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 2</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">4</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 3</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 5</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">6</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 8</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">7</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 13</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">8</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 21</span>\n\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">20</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 6_765</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">38</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 39_088_169</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> ~450ms on my machine 😬</span>\n</span></code></pre>\n<p>Demonstration of the lack of memoization:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> log <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  log<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> log<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">+</span> <span class=\"token number\">1</span> <span class=\"token operator\">||</span> <span class=\"token number\">1</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> n\n  <span class=\"token keyword\">return</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">10</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 55</span>\n\nObject<span class=\"token punctuation\">.</span><span class=\"token function\">entries</span><span class=\"token punctuation\">(</span>log<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">,</span> times<span class=\"token punctuation\">]</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">fib(</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>n<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">) was called </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>times<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> times</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> fib(10) was called 1 times</span>\n<span class=\"token comment\">//=> fib(9) was called 1 times</span>\n<span class=\"token comment\">//=> fib(8) was called 2 times</span>\n<span class=\"token comment\">//=> fib(7) was called 3 times</span>\n<span class=\"token comment\">//=> fib(6) was called 5 times</span>\n<span class=\"token comment\">//=> fib(5) was called 8 times</span>\n<span class=\"token comment\">//=> fib(4) was called 13 times</span>\n<span class=\"token comment\">//=> fib(3) was called 21 times</span>\n<span class=\"token comment\">//=> fib(2) was called 34 times</span>\n<span class=\"token comment\">//=> fib(1) was called 55 times</span>\n<span class=\"token comment\">//=> fib(0) was called 34 times</span>\n</span></code></pre>\n<p>It's funny that those numbers –\nhow many times <code>fib(n)</code> was called –\nfollow the Fibonacci sequence!\n(Excluding the last one;\nlooks like the numbers could continue decrementing at the same rate?\nInteresting.)</p>\n<p>No wonder calculating <code>fib(38)</code> takes half a second.\n<code>fib(1)</code> gets called 39,088,169 times!</p>\n<h2 id=\"step-2-memoization\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-2-memoization\">Step 2: Memoization</a></h2>\n<p>Diff:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> n\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>  cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>  <span class=\"token keyword\">return</span> cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token punctuation\">}</span>\n</span></span></code></pre>\n<p>Result:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> n\n  cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p><code>cache</code> is a &quot;global&quot; object for now.\nWe'll change it later.</p>\n<p>Performance is much better now:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">console<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">38</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 39_088_169</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> Less than 1ms on my machine 😎</span>\n</span></code></pre>\n<p>Demonstration of the presence of memoization:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> log <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  log<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> log<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">+</span> <span class=\"token number\">1</span> <span class=\"token operator\">||</span> <span class=\"token number\">1</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> n\n  cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">20</span><span class=\"token punctuation\">)</span>\n\nObject<span class=\"token punctuation\">.</span><span class=\"token function\">entries</span><span class=\"token punctuation\">(</span>log<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">reverse</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">,</span> times<span class=\"token punctuation\">]</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">fib(</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>n<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">) was called </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>times<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\"> times</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> fib(10) was called 1 times</span>\n<span class=\"token comment\">//=> fib(9) was called 1 times</span>\n<span class=\"token comment\">//=> fib(8) was called 2 times</span>\n<span class=\"token comment\">//=> fib(7) was called 2 times</span>\n<span class=\"token comment\">//=> fib(6) was called 2 times</span>\n<span class=\"token comment\">//=> fib(5) was called 2 times</span>\n<span class=\"token comment\">//=> fib(4) was called 2 times</span>\n<span class=\"token comment\">//=> fib(3) was called 2 times</span>\n<span class=\"token comment\">//=> fib(2) was called 2 times</span>\n<span class=\"token comment\">//=> fib(1) was called 2 times</span>\n<span class=\"token comment\">//=> fib(0) was called 1 times</span>\n</span></code></pre>\n<h2 id=\"step-3-golf\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-3-golf\">Step 3: Golf ⛳</a></h2>\n<p>Code golfing = striving for the shortest possible source code.</p>\n<h3 id=\"step-31-logical-or-assignment-operator-oror\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-31-logical-or-assignment-operator-oror\">Step 3.1: Logical OR assignment operator (<code>||=</code>)</a></h3>\n<p>The <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR_assignment\" class=\"link link-external\">logical OR assignment operator</a>\nwas introduced in the ECMAScript 2021 spec<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup>\nand is perfect for our case.</p>\n<p>Diff:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> n\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>  cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>  cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>  <span class=\"token keyword\">return</span> cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token punctuation\">}</span>\n</span></span></code></pre>\n<p>Result:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> n\n  cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h3 id=\"step-32-return-value-of-the-assignment-operator\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-32-return-value-of-the-assignment-operator\">Step 3.2: Return value of the assignment operator</a></h3>\n<p>In JavaScript,\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#return_value_and_chaining\" class=\"link link-external\">assignments have return values</a>:</p>\n<blockquote>\n<p>Like most expressions,\nassignments like <code>x = y</code> have a return value.</p>\n<p>[...]</p>\n<p>That means that <code>(x = y)</code> returns <code>y</code>,\n<code>(x += y)</code> returns the resulting sum <code>x + y</code>,\n<code>(x **= y)</code> returns the resulting power <code>x ** y</code>,\nand so on.</p>\n<p>In the case of logical assignments,\n<code>(x &amp;&amp;= y)</code>, <code>(x ||= y)</code>, and <code>(x ??= y)</code>,\nthe return value is that of the logical operation without the assignment,\nso <code>x &amp;&amp; y</code>, <code>x || y</code>, and <code>x ?? y</code>,\nrespectively.</p>\n</blockquote>\n<p>Diff:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> n\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>  cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix deleted\">-</span>  <span class=\"token keyword\">return</span> cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token punctuation\">}</span>\n</span></span></code></pre>\n<p>Result:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>The outermost parentheses in the return statement are added by Prettier,\nbut they are optional.</p>\n<h3 id=\"step-33-ternary-operator\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-33-ternary-operator\">Step 3.3: Ternary operator</a></h3>\n<p>Diff:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix deleted\">-</span>  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> n\n<span class=\"token prefix deleted\">-</span>  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix deleted\">-</span><span class=\"token punctuation\">}</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span> <span class=\"token operator\">?</span> n <span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></span></code></pre>\n<p>Result:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span> <span class=\"token operator\">?</span> n <span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h3 id=\"step-34-pass-cache-as-a-parameter\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-34-pass-cache-as-a-parameter\">Step 3.4: Pass cache as a parameter</a></h3>\n<p>Diff:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token prefix deleted\">-</span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span> <span class=\"token operator\">?</span> n <span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n<span class=\"token punctuation\">,</span> _cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token prefix inserted\">+</span>  n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span> <span class=\"token operator\">?</span> n <span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>_cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></span></code></pre>\n<p>Result:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n<span class=\"token punctuation\">,</span> _cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span> <span class=\"token operator\">?</span> n <span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>_cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Yay,\nwe got rid of the &quot;global&quot; <code>cache</code> object.</p>\n<p>The downside of passing cache as a parameter\nis that the cache is discarded afterwards.\nThis reduces performance if <code>fib()</code> is called many times:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Before (global cache):</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nArray<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">50_000</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">1_000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ~5ms</span>\n\n<span class=\"token comment\">// After (cache as parameter):</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nArray<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">50_000</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">1_000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ~2,000ms</span>\n</span></code></pre>\n<p>Okay,\nthat's silly.\nWho would call <code>fib(1_000)</code> 50,000 times? 😅\nBut the point remains that the performance is worse.</p>\n<p>The new function parameter is prefixed with an underscore\nto denote that it's a &quot;private&quot; parameter\nnot meant to be used by the user of the function.</p>\n<p>Though it <em>can</em> be used,\nand doing so will fix the performance problem:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">console<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\nArray<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">length</span><span class=\"token operator\">:</span> <span class=\"token number\">50_000</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">1_000</span><span class=\"token punctuation\">,</span> cache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ~5ms</span>\n</span></code></pre>\n<p>I think an optional cache parameter\nis the best of both worlds:\nyou can just call <code>fib(n)</code> and not have to care about creating a cache object,\nbut you can also easily create a persistent cache if you need to.</p>\n<h3 id=\"step-35-obfuscate-the-code-to-fit-on-a-single-line\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#step-35-obfuscate-the-code-to-fit-on-a-single-line\">Step 3.5: Obfuscate the code to fit on a single line 😅</a></h3>\n<p>We are almost there!\nThe code from the previous step could already be placed on a single line,\nbut I want to let Prettier format my code.</p>\n<p>Renaming <code>_cache</code> to <code>c</code> <em>almost</em> makes the code fit on a single line:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n<span class=\"token punctuation\">,</span> c <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span> <span class=\"token operator\">?</span> n <span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>c<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> c<span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> c<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>It's only 1 character too long!</p>\n<p>We can further reduce the code's length\nby using the <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Decrement\" class=\"link link-external\">decrement operator</a>\ninstead of <code>n - 1</code> and <code>n - 2</code>.\nNote that we have to use it in prefix position:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n<span class=\"token punctuation\">,</span> c <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span> <span class=\"token operator\">?</span> n <span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>c<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token operator\">--</span>n<span class=\"token punctuation\">,</span> c<span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token operator\">--</span>n<span class=\"token punctuation\">,</span> c<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Now the code fits on a single line! 💪</p>\n<p>We could golf more by\nchanging <code>const</code> to <code>let</code>,\nrenaming <code>fib</code> to <code>f</code>,\nand removing unnecessary spaces and parentheses:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">let</span> <span class=\"token function-variable function\">f</span><span class=\"token operator\">=</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">n<span class=\"token punctuation\">,</span>c<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span><span class=\"token operator\">=></span>n<span class=\"token operator\">&lt;</span><span class=\"token number\">2</span><span class=\"token operator\">?</span>n<span class=\"token operator\">:</span>c<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span><span class=\"token operator\">||=</span><span class=\"token function\">f</span><span class=\"token punctuation\">(</span><span class=\"token operator\">--</span>n<span class=\"token punctuation\">,</span>c<span class=\"token punctuation\">)</span><span class=\"token operator\">+</span><span class=\"token function\">f</span><span class=\"token punctuation\">(</span><span class=\"token operator\">--</span>n<span class=\"token punctuation\">,</span>c<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>But that would be silly.</p>\n<p>In fact,\nthis whole step 3.5 is kind of silly.\nI would prefer the code from <a href=\"#step-34-pass-cache-as-a-parameter\" class=\"link link-anchor\">step 3.4</a>.\nIt spans on two lines,\nbut at least it's readable.</p>\n<h2 id=\"bonuses\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#bonuses\">Bonuses</a></h2>\n<p>These are bonuses,\nso we don't necessarily have to aim for the line length of one. 😜</p>\n<h3 id=\"bonus-1-arbitrarily-large-integers-with-bigints\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#bonus-1-arbitrarily-large-integers-with-bigints\">Bonus 1: Arbitrarily large integers with BigInts</a></h3>\n<p>The <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER\" class=\"link link-external\"><code>Number.MAX_SAFE_INTEGER</code> constant</a>\nrepresents the maximum safe integer in JavaScript (<code>2^53 - 1</code>).</p>\n<blockquote>\n<p>Safe in this context refers to\nthe ability to represent integers exactly and to correctly compare them.\nFor example,\n<code>Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2</code> will evaluate to true,\nwhich is mathematically incorrect.</p>\n</blockquote>\n<p><code>2^53 - 1</code> sounds large,\nbut it's exceeded quite quickly when calculating Fibonacci numbers:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">78</span><span class=\"token punctuation\">)</span>                 <span class=\"token comment\">//=>  8_944_394_323_791_464</span>\nNumber<span class=\"token punctuation\">.</span><span class=\"token constant\">MAX_SAFE_INTEGER</span> <span class=\"token comment\">//=>  9_007_199_254_740_991</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">79</span><span class=\"token punctuation\">)</span>                 <span class=\"token comment\">//=> 14_472_334_024_676_220 (incorrect!)</span>\n</span></code></pre>\n<p><a href=\"https://duckduckgo.com/?q=fibonacci+79&amp;ia=answer\" class=\"link link-external\">A quick Duck search</a>\nshows that our result for <code>fib(79)</code> is incorrect;\nthe correct value is our result plus one.</p>\n<p>We should instead use\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt\" class=\"link link-external\">BigInt values</a>,\nintroduced in the ECMAScript 2020 spec<sup><a aria-label=\"footnote 2\" class=\"link\" href=\"#fn-2\" id=\"fnref-2\">[2]</a></sup>.\nBigInts can be used for arbitrarily large integers.</p>\n<p>BigInts can be created in two ways:</p>\n<ul>\n<li>by appending <code>n</code> to the end of an integer literal (<code>100n</code>)</li>\n<li>by calling the <code>BigInt()</code> constructor without the <code>new</code> operator\n(<code>BigInt(100)</code>, <code>BigInt(100n)</code> or <code>BigInt('100')</code>).</li>\n</ul>\n<p>BigInts can't be mixed with regular numbers,\nso we have to change <code>n - 1</code> to <code>n - 1n</code>\nand <code>n - 2</code> to <code>n - 2n</code>.\nHowever,\nBigInts <em>can</em> be compared with regular numbers,\nso we don't have to change <code>n &lt; 2</code>.</p>\n<p>After introducing BigInts to the code from <a href=\"#step-34-pass-cache-as-a-parameter\" class=\"link link-anchor\">step 3.4</a>,\nthe code looks like this:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n<span class=\"token punctuation\">,</span> _cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span> <span class=\"token operator\">?</span> n <span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span>_cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1n</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2n</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">78n</span><span class=\"token punctuation\">)</span>                <span class=\"token comment\">//=>  8_472_334_024_676_464n</span>\nNumber<span class=\"token punctuation\">.</span><span class=\"token constant\">MAX_SAFE_INTEGER</span> <span class=\"token comment\">//=>  9_472_334_024_676_991</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">79n</span><span class=\"token punctuation\">)</span>                <span class=\"token comment\">//=> 14_472_334_024_676_221n (correct!)</span>\n\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">1_000n</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 43_466_557_686_937_472_334_024_676_675_472_334_024_676_660_472_334_024_676_481_472_334_024_676_417_472_334_024_676_879_472_334_024_676_295_472_334_024_676_634_472_334_024_676_239_472_334_024_676_642_472_334_024_676_187_472_334_024_676_928_472_334_024_676_137_472_334_024_676_875n 😅</span>\n</span></code></pre>\n<p>Performance seems about the same.\nThe above code runs in less than 2ms on my machine.</p>\n<p>Note that our function has to be called with a BigInt.\nCalling it with a regular number will throw a TypeError:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">100</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Chrome: \"Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions\"</span>\n<span class=\"token comment\">//=> Firefox: \"Uncaught TypeError: can't convert BigInt to number\"</span>\n</span></code></pre>\n<p>We'll change this in the <a href=\"#bonus-3-input-validation-and-typescript-types\" class=\"link link-anchor\">3rd bonus section</a>.</p>\n<h3 id=\"bonus-2-avoiding-stack-overflows\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#bonus-2-avoiding-stack-overflows\">Bonus 2: Avoiding stack overflows</a></h3>\n<p>We can now support arbitrarily large integers,\nexcept we can't\nbecause too large inputs\ncause stack overflow errors:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">10_000n</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Chrome: \"Uncaught RangeError: Maximum call stack size exceeded\"</span>\n<span class=\"token comment\">//=> Firefox: No problem on my machine!</span>\n\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">100_000n</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Chrome: \"Uncaught RangeError: Maximum call stack size exceeded\"</span>\n<span class=\"token comment\">//=> Firefox: \"Uncaught InternalError: too much recursion\"</span>\n</span></code></pre>\n<p>Here's what we can do:</p>\n<ul>\n<li>Make our function <code>async</code>.</li>\n<li>Periodically pause our function with <code>await</code>, Promises and timeouts,\nso that the call stack can be periodically emptied.</li>\n</ul>\n<p>Like so; I put a log statement for demonstration\n(notice also the non-highlighted <code>async</code> and <code>await</code> keywords):</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n<span class=\"token punctuation\">,</span> _cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">%</span> <span class=\"token number\">1_000n</span> <span class=\"token operator\">===</span> <span class=\"token number\">0n</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Pausing at n ='</span><span class=\"token punctuation\">,</span> n<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">await</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n  _cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1n</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2n</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> _cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Using <code>await</code> inside the second <code>if</code> statement\npauses the function until the new Promise is resolved,\ni.e. until <code>resolve()</code> is called.</p>\n<p>Using <code>setTimeout()</code> puts the calling of <code>resolve()</code>\nto the end of the browser's task queue.\nTasks are processed only when the call stack is empty.\nIn other words,\nour function resumes only after the call stack has been emptied,\nso now we won't get stack overflow errors.</p>\n<p>If you are not following,\nplease read Jake Archibald's great article\n<a href=\"https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/\" class=\"link link-external\"><em>Tasks, microtasks, queues and schedules</em></a>.</p>\n<p>Usage:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">console<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">100n</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> 354_224_848_179_261_915_075n</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ~10ms</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">5_000n</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Pausing at n = 5000n</span>\n<span class=\"token comment\">//=> Pausing at n = 4000n</span>\n<span class=\"token comment\">//=> Pausing at n = 3000n</span>\n<span class=\"token comment\">//=> Pausing at n = 2000n</span>\n<span class=\"token comment\">//=> Pausing at n = 1000n</span>\n<span class=\"token comment\">//=> Pausing at n = 1000n</span>\n<span class=\"token comment\">//=> Pausing at n = 2000n</span>\n<span class=\"token comment\">//=> Pausing at n = 3000n</span>\n<span class=\"token comment\">//=> Pausing at n = 4000n</span>\n<span class=\"token comment\">//=> (returns a big-ass number)</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ~1,200ms</span>\n</span></code></pre>\n<p>Two problems though:</p>\n<ul>\n<li>The function is now very slow.</li>\n<li>The page becomes janky with large inputs (e.g. <code>fib(5_000n)</code>).</li>\n</ul>\n<p>Jankiness could be reduced by pausing more often\n(e.g. <code>if (n % 100n === 0n)</code>),\nbut it would make the function even slower\n(more pausing means more waiting).</p>\n<p>At this point it would anyway be better to use a non-recursive function\n(see the <a href=\"#bonus-4-non-recursive-non-memoized-function\" class=\"link link-anchor\">4th bonus section</a>).\nIt would be much faster\nand would have no stack overflow problems.\nMove it to a Web Worker\nand the page wouldn't become janky even with enormous inputs.</p>\n<h4>Sidetrack: <code>await new Promise(setTimeout)</code></h4>\n<p>If we were code golfing,\nwe could shorten our pausing code like this:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Before</span>\n<span class=\"token keyword\">await</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// After</span>\n<span class=\"token keyword\">await</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span>setTimeout<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>The shortened code works\nbecause the <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise\" class=\"link link-external\">Promise constructor</a>\ntakes one function parameter (<code>executor</code>),\nwhich in turn takes two function parameters\n(<code>resolutionFunc</code> and <code>rejectionFunc</code>).\nSo, <code>new Promise(setTimeout)</code> expands to:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolutionFunc<span class=\"token punctuation\">,</span> rejectionFunc</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolutionFunc<span class=\"token punctuation\">,</span> rejectionFunc<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p><code>setTimeout</code>'s second parameter (<code>delay</code> in milliseconds)\ngets coerced into a number,\nso basically <code>Number(rejectionFunc)</code><sup><a aria-label=\"footnote 3\" class=\"link\" href=\"#fn-3\" id=\"fnref-3\">[3]</a></sup>,\nwhich is <code>NaN</code>,\nwhich is the same as omitting the second parameter.</p>\n<p>Of course,\nthe shorter code shouldn't be generally used\nbecause it's too cryptic.</p>\n<h3 id=\"bonus-3-input-validation-and-typescript-types\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#bonus-3-input-validation-and-typescript-types\">Bonus 3: Input validation and TypeScript types</a></h3>\n<p>As mentioned in <a href=\"#step-1-recursive-function-without-memoization\" class=\"link link-anchor\">step 1</a>,\nnegative inputs are returned as-is,\nand non-number inputs may break the function.\nLet's add input validation:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n<span class=\"token punctuation\">,</span> _cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'n must be non-negative'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> n\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">%</span> <span class=\"token number\">1_000n</span> <span class=\"token operator\">===</span> <span class=\"token number\">0n</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">await</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n  _cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1n</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2n</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> _cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=>  Error: n must be non-negative</span>\n<span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">1n</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> Error: n must be non-negative</span>\n</span></code></pre>\n<p>It would be nice to accept regular numbers as well.\nIt would be no &quot;validation,&quot;\nbut let's add that:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n<span class=\"token punctuation\">,</span> _cache <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'n must be non-negative'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token function\">BigInt</span><span class=\"token punctuation\">(</span>n<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">typeof</span> n <span class=\"token operator\">!==</span> <span class=\"token string\">'bigint'</span><span class=\"token punctuation\">)</span> n <span class=\"token operator\">=</span> <span class=\"token function\">BigInt</span><span class=\"token punctuation\">(</span>n<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">%</span> <span class=\"token number\">1_000n</span> <span class=\"token operator\">===</span> <span class=\"token number\">0n</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">await</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">resolve</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n  _cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1n</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2n</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> _cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">10</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=>  55n</span>\n<span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">10n</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 55n</span>\n</span></code></pre>\n<p>Now that our function is ready,\nlet's add TypeScript types:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> fib <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span>\n  n<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token operator\">|</span> bigint<span class=\"token punctuation\">,</span>\n  _cache<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token punctuation\">[</span>key<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">]</span><span class=\"token operator\">:</span> bigint <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span><span class=\"token operator\">:</span> <span class=\"token builtin\">Promise</span><span class=\"token operator\">&lt;</span>bigint<span class=\"token operator\">></span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">'n must be non-negative'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">&lt;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token function\">BigInt</span><span class=\"token punctuation\">(</span>n<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">typeof</span> n <span class=\"token operator\">!==</span> <span class=\"token string\">'bigint'</span><span class=\"token punctuation\">)</span> n <span class=\"token operator\">=</span> <span class=\"token function\">BigInt</span><span class=\"token punctuation\">(</span>n<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>n <span class=\"token operator\">%</span> <span class=\"token number\">1_000n</span> <span class=\"token operator\">===</span> <span class=\"token number\">0n</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">await</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\"><span class=\"token builtin\">Promise</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span>resolve<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n  _cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">||=</span>\n    <span class=\"token punctuation\">(</span><span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">1n</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">await</span> <span class=\"token function\">fib</span><span class=\"token punctuation\">(</span>n <span class=\"token operator\">-</span> <span class=\"token number\">2n</span><span class=\"token punctuation\">,</span> _cache<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> _cache<span class=\"token punctuation\">[</span>n<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Eh, quite ugly and way more than 1 line (14). 😅</p>\n<p>Note that instead of <code>_cache[n]</code>\nI had to explicitly use <code>_cache[n.toString()]</code>.\nA reason for this can be found in the\n<a href=\"https://basarat.gitbook.io/typescript/type-system/index-signatures#typescript-index-signature\" class=\"link link-external\"><em>TypeScript Index Signature</em> section of the <em>TypeScript Deep Dive</em> book</a>.</p>\n<h3 id=\"bonus-4-non-recursive-non-memoized-function\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#bonus-4-non-recursive-non-memoized-function\">Bonus 4: Non-recursive non-memoized function</a></h3>\n<p>I wanted to see how much faster a non-recursive function would be:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fib</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">n</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> a <span class=\"token operator\">=</span> <span class=\"token number\">0n</span>\n  <span class=\"token keyword\">let</span> b <span class=\"token operator\">=</span> <span class=\"token number\">1n</span>\n  <span class=\"token keyword\">let</span> c <span class=\"token operator\">=</span> <span class=\"token number\">0n</span>\n  <span class=\"token keyword\">while</span> <span class=\"token punctuation\">(</span>n<span class=\"token operator\">--</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">[</span>a<span class=\"token punctuation\">,</span> b<span class=\"token punctuation\">,</span> c<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>b<span class=\"token punctuation\">,</span> c<span class=\"token punctuation\">,</span> b <span class=\"token operator\">+</span> c<span class=\"token punctuation\">]</span>\n  <span class=\"token keyword\">return</span> c\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">10</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 55n (correct)</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">5_000</span><span class=\"token punctuation\">)</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> ~3ms</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">time</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token function\">fib</span><span class=\"token punctuation\">(</span><span class=\"token number\">100_000</span><span class=\"token punctuation\">)</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">timeEnd</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> ~90ms</span>\n</span></code></pre>\n<p>Much faster,\nso this whole blog post was a bit silly in the end. 🙈</p>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>See\n<a href=\"https://github.com/tc39/proposals/blob/master/finished-proposals.md\" class=\"link link-external\">list of finished ECMAScript proposals on GitHub</a>. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n<li id=\"fn-2\"><p>See previous footnote. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-2\">↩</a></p>\n</li>\n<li id=\"fn-3\"><p>See Dr. Axel Rauschmayer's article\n<a href=\"https://2ality.com/2019/10/type-coercion.html\" class=\"link link-external\"><em>Type coercion in JavaScript</em></a>\nfor details. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-3\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"Using recursion,\nlogical OR assignment operator (`||=`),\nBigInts\nand more!","date_published":"2021-08-05T12:00:00+03:00","tags":["JavaScript","TypeScript"]},{"id":"https://mtsknn.fi/blog/11ty-vs-eleventy/","url":"https://mtsknn.fi/blog/11ty-vs-eleventy/","title":"11ty vs Eleventy","content_html":"<p>The two names are often used interchangeably,\nbut there's a slight difference:\n11ty is the organization,\nEleventy is the static site generator.</p>\n<p>I used to use the name &quot;11ty&quot;\nwhen talking about\n<a href=\"https://www.11ty.dev/\" class=\"link link-external\">Eleventy, the static site generator</a>.\nApparently that's slightly wrong;\nfrom <a href=\"https://github.com/11ty/eleventy/issues/921#issuecomment-879070567\" class=\"link link-external\">a GitHub comment by Zach Leatherman (the author of Eleventy)</a>:</p>\n<blockquote>\n<p>11ty is the org and eleventy is the software.\nI know that's confusing.\nIn retrospect it may have been nicer to use <code>11ty/11ty</code> but I didn't 😅.</p>\n</blockquote>\n<p>There you have it.\nFrom now on\nI'm going to use the name &quot;Eleventy&quot;\nwhen talking about the static site generator.\nA bit shame,\nsince &quot;11ty&quot; looks better.</p>\n<p>When to use the name &quot;11ty&quot; then?\nDunno,\nI rarely talk about the organization.</p>\n<p>By the way,\nsome extra confusion before you go:</p>\n<ul>\n<li><a href=\"https://www.11ty.dev/\" class=\"link link-external\">Eleventy's website</a>\nis at 11ty.dev.</li>\n<li><a href=\"https://github.com/11ty/eleventy/\" class=\"link link-external\">Eleventy's GitHub page</a>\nhas a big &quot;11ty&quot; logo at the top.</li>\n<li><a href=\"https://twitter.com/eleven_ty/\" class=\"link link-external\">Eleventy's Twitter account</a>\nhas an &quot;11ty&quot; avatar.</li>\n</ul>\n","summary":"The two names are often used interchangeably,\nbut there's a slight difference:\n11ty is the organization,\nEleventy is the static site generator.","date_published":"2021-08-01T12:00:00+03:00","tags":["Eleventy"]},{"id":"https://mtsknn.fi/blog/how-to-render-optimizely-content-recommendations-using-react/","url":"https://mtsknn.fi/blog/how-to-render-optimizely-content-recommendations-using-react/","title":"How to render Optimizely Content Recommendations using React","content_html":"<p>Also known as Episerver/Idio Content Recommendations.\nThe usual way of using a Mustache template\ncan be circumvented by using a JavaScript proxy.</p>\n<h2 id=\"what-is-optimizely-content-recommendations\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#what-is-optimizely-content-recommendations\">What is Optimizely Content Recommendations?</a></h2>\n<p><a href=\"https://www.episerver.com/products/capabilities/web-content-recommendations\" class=\"link link-external\">Episerver's marketing page describes Web Content Recommendations</a>\nlike so:</p>\n<blockquote>\n<p>Serve each visitor the most relevant content automatically.\nAI-powered recommendations\nact upon the unique interests of each visitor in real-time\nto help you deliver personalization with minimal manual effort.</p>\n</blockquote>\n<p><a href=\"https://world.episerver.com/documentation/developer-guides/personalization/content-recommendations/\" class=\"link link-external\">Optimizely's developer docs describe Content Recommendations</a>\nmaybe slightly more clearly:</p>\n<blockquote>\n<p>Optimizely Content Recommendations\nautomatically generates a personalized content feed for each visitor\nbased on the individual's site activity.</p>\n<p>Content Recommendations uses Natural Language Processing (NLP)\nto understand the meaning of each piece of content at a granular level\nand builds a real-time interest profile for each visitor\nbased on their interactions with the NLP-generated topics.\nContent Recommendations uses this information to recommend\narticles, blog posts, or other specified content sections\nthat are most relevant to the visitor's interest profile.</p>\n</blockquote>\n<p><strong>TL;DR:</strong>\nPersonalized content recommendations\nbased on visitor tracking\nand natural language processing of the content pages.</p>\n<h3 id=\"optimizely-vs-episerver-vs-idio\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#optimizely-vs-episerver-vs-idio\">Optimizely vs Episerver vs Idio</a></h3>\n<p>Optimizely Content Recommendations\nhave been previously called Episerver Content Recommendations,\nand before that Idio Content Recommendations.\nSome context:</p>\n<ul>\n<li><a href=\"https://www.episerver.com/campaign/idio\" class=\"link link-external\">Episerver acquired a company called Idio in November 2019.</a></li>\n<li><a href=\"https://www.episerver.com/campaign/optimizely\" class=\"link link-external\">Episerver acquired a company called Optimizely in October 2020</a>\nand now\n<a href=\"https://www.episerver.com/campaign/becoming-optimizely\" class=\"link link-external\">Episerver is rebranding itself as Optimizely</a>.</li>\n</ul>\n<h2 id=\"usual-implementation-mustache-template\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#usual-implementation-mustache-template\">Usual implementation: Mustache template</a></h2>\n<p>On pages where you want to see intelligent content recommendations:</p>\n<ol>\n<li>\n<p>Add a <a href=\"https://mustache.github.io/mustache.5.html\" class=\"link link-external\">Mustache template</a>\nwith the class <code>idio-recommendations</code>.\nExample:</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>script</span>\n  <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>idio-recommendations<span class=\"token punctuation\">\"</span></span>\n  <span class=\"token attr-name\">data-api-key</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>...<span class=\"token punctuation\">\"</span></span>\n  <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>text/x-mustache<span class=\"token punctuation\">\"</span></span>\n<span class=\"token punctuation\">></span></span><span class=\"token script\"><span class=\"token language-javascript\">\n  <span class=\"token operator\">&lt;</span>div <span class=\"token keyword\">class</span><span class=\"token operator\">=</span><span class=\"token string\">\"recommendation\"</span><span class=\"token operator\">></span>\n    <span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span>#content<span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span>\n      <span class=\"token operator\">&lt;</span>h2<span class=\"token operator\">></span>\n        <span class=\"token operator\">&lt;</span>a href<span class=\"token operator\">=</span><span class=\"token string\">\"{{link_url}}\"</span><span class=\"token operator\">></span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span>title<span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span>a<span class=\"token operator\">></span>\n      <span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span>h2<span class=\"token operator\">></span>\n      <span class=\"token operator\">&lt;</span>img alt<span class=\"token operator\">=</span><span class=\"token string\">\"\"</span> src<span class=\"token operator\">=</span><span class=\"token string\">\"{{main_image_url}}\"</span> <span class=\"token operator\">/</span><span class=\"token operator\">></span>\n      <span class=\"token operator\">&lt;</span>p<span class=\"token operator\">></span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span>abstract<span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span>p<span class=\"token operator\">></span>\n      <span class=\"token operator\">&lt;</span>p<span class=\"token operator\">></span>\n        Published on <span class=\"token operator\">&lt;</span>span <span class=\"token keyword\">class</span><span class=\"token operator\">=</span><span class=\"token string\">\"date\"</span><span class=\"token operator\">></span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span>published<span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span>span<span class=\"token operator\">></span>\n      <span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span>p<span class=\"token operator\">></span>\n    <span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span><span class=\"token operator\">/</span>content<span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span>div<span class=\"token operator\">></span>\n</span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>script</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n</li>\n<li>\n<p>Load <code>ip.js</code> script (&quot;Idio Personalization&quot;).</p>\n</li>\n</ol>\n<p>When the page is loaded,\nthe <code>ip.js</code> script will\nlook for the Mustache template,\nand if found,\nload content recommendations via an Ajax request\nand render the Mustache template with the loaded data.</p>\n<p>Quite straightforward.\nIf this works for you,\ngreat!</p>\n<h3 id=\"problems-when-using-with-react\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problems-when-using-with-react\">Problems when using with React</a></h3>\n<p>Rendering a Mustache template to the page using React is not a problem.\nSome other basic things are however difficult:</p>\n<ul>\n<li>How to format dates in the Mustache template?\nThey are rendered in the <a href=\"https://en.wikipedia.org/wiki/ISO_8601\" class=\"link link-external\">ISO 8601</a> format by default\nand I haven't found a way to customize that.</li>\n<li>How to handle links in the Mustache template\nso that the links are handled like SPA (single page app) links?</li>\n</ul>\n<p>I suspect that other basic things would also be difficult.</p>\n<h2 id=\"naive-and-hacky-react-implementation-not-recommended\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#naive-and-hacky-react-implementation-not-recommended\">Naive and hacky React implementation (not recommended)</a></h2>\n<p><strong>I don't recommend this approach.</strong>\nCheck the next section for a better approach.\nI'm documenting this here\nbecause this is what I tried first.</p>\n<p>Content Recommendations offers a way to run your own logic\nafter the Mustache template has been populated with data.</p>\n<p>After <code>ip.js</code> has loaded the data via Ajax\nand rendered the Mustache template,\nit will look for a <code>window._ipc</code> object\n(apparently stands for &quot;Idio Personalization Config&quot;)\nand call its <code>complete</code> function (if it exists) with two arguments:\na reference to the DOM element containing the recommendations,\nand a status code of the Ajax response.</p>\n<p>By utilizing the <code>window._ipc</code> object,\nit should be possible to\ndo these steps after the Mustache template has been rendered:</p>\n<ul>\n<li>Format dates inside the DOM element by mutating them.</li>\n<li>Attach click event listeners to the links inside the DOM element\nfor SPA-like navigation.</li>\n</ul>\n<p>Something like this (quick and dirty code):</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">ContentRecommendations</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">useIdioPersonalizationConfig</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>script</span>\n      <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>idio-recommendations<span class=\"token punctuation\">\"</span></span>\n      <span class=\"token attr-name\">data-api-key</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>...<span class=\"token punctuation\">\"</span></span>\n      <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>text/x-mustache<span class=\"token punctuation\">\"</span></span>\n    <span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      ...\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>script</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">useIdioPersonalizationConfig</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">let</span> links <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n\n    window<span class=\"token punctuation\">.</span>_ipc <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">complete</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">element<span class=\"token punctuation\">,</span> statusCode</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>element <span class=\"token operator\">||</span> statusCode <span class=\"token operator\">!==</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span>\n\n        element<span class=\"token punctuation\">.</span><span class=\"token function\">querySelectorAll</span><span class=\"token punctuation\">(</span><span class=\"token string\">'.date'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">date</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n          date<span class=\"token punctuation\">.</span>textContent <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span>date<span class=\"token punctuation\">.</span>textContent<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toLocaleString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n        links <span class=\"token operator\">=</span> element<span class=\"token punctuation\">.</span><span class=\"token function\">querySelectorAll</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">)</span>\n        links<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">link</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n          link<span class=\"token punctuation\">.</span><span class=\"token function\">addEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleLinkClick<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      links<span class=\"token punctuation\">.</span><span class=\"token function\">forEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">link</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        link<span class=\"token punctuation\">.</span><span class=\"token function\">removeEventListener</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> handleLinkClick<span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Notice how I have to remove the links' click event listeners\nin the <code>useEffect</code>'s cleanup function\nto avoid memory leaks.</p>\n<p>Is this good?\nNah, it's very hacky.\nI don't like to see this kind of code in a React project.</p>\n<p>If there are more things that you have to do like this –\nthings that you can't do with just the Mustache template –\nthe code becomes even uglier.</p>\n<h2 id=\"better-albeit-still-hacky-react-implementation-recommended\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#better-albeit-still-hacky-react-implementation-recommended\">Better (albeit still hacky) React implementation (recommended)</a></h2>\n<p>It would be nice\nif we could do the Ajax requests ourselves,\nbut that's not possible.\nPlus in the end\nI think it's best to leave the request logic to <code>ip.js</code>.</p>\n<p>It would be even better\nif we could get the Ajax response's data directly\ninstead of letting <code>ip.js</code> use it to render the Mustache template.\nBut that's not possible...\nunless we use a certain hack. 🤪</p>\n<h3 id=\"the-flow-of-things\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-flow-of-things\">The flow of things</a></h3>\n<p>I did some digging,\nand the flow goes like this:</p>\n<ol>\n<li>\n<p>After the page has loaded,\n<code>ip.js</code> does some initializations like this:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">window<span class=\"token punctuation\">.</span>idio <span class=\"token operator\">=</span> window<span class=\"token punctuation\">.</span>idio <span class=\"token operator\">||</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\nwindow<span class=\"token punctuation\">.</span>idio<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">render</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token comment\">/* ... */</span> <span class=\"token punctuation\">}</span>\nwindow<span class=\"token punctuation\">.</span>idio<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">load</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span> <span class=\"token comment\">/* ... */</span> <span class=\"token punctuation\">}</span>\n<span class=\"token comment\">// ...</span>\n</span></code></pre>\n<p>Notice how <code>ip.js</code> doesn't override the <code>window.idio</code> object\nif it already exists.</p>\n</li>\n<li>\n<p><code>ip.js</code> looks for Mustache templates\nwith the class <code>idio-recommendations</code>.\n(Actually, it looks for any <code>script</code> elements\nwith that class.)</p>\n</li>\n<li>\n<p>If a template is found,\n<code>ip.js</code> does an Ajax request to get content recommendations for the user.</p>\n</li>\n<li>\n<p>The Ajax response contains JavaScript code like this:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">window<span class=\"token punctuation\">.</span>idio<span class=\"token punctuation\">.</span><span class=\"token function\">r0</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">total_hits</span><span class=\"token operator\">:</span> <span class=\"token number\">1234</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">content</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token punctuation\">{</span> <span class=\"token comment\">/* ... */</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">{</span> <span class=\"token comment\">/* ... */</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">// ...</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token number\">200</span>\n<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>I.e. the returned code calls <code>window.idio.r0</code>\nwith the content recommendations data\nand a status code of the Ajax response.</p>\n</li>\n<li>\n<p><code>window.idio.r0</code> renders the Mustache template with the data.</p>\n</li>\n</ol>\n<p>If the page contains more than one Mustache template,\nthe first Ajax response calls <code>window.idio.r0</code>,\nthe second one calls <code>window.idio.r1</code>,\nand so on.\nSo the &quot;r&quot; is apparently &quot;render&quot; or &quot;result,&quot;\nand the number is a 0-based index.</p>\n<h3 id=\"the-hack-proxying-windowidio\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-hack-proxying-windowidio\">The hack: proxying <code>window.idio</code></a></h3>\n<p>So,\neverything is fine to us\nexcept the last step.\nIf only we could do something else\nthan let <code>ip.js</code> render the Mustache template\nwhen <code>window.idio.r0</code> is called...</p>\n<p>Well, that's what\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy\" class=\"link link-external\">JavaScript proxies</a>\nare for!\nWe can use one to make <code>window.idio.r0</code> return something else\nwhen it's accessed.</p>\n<p>As we saw earlier,\n<code>ip.js</code> doesn't override the <code>window.idio</code> object\nif it already exists.\nSo we can define it ourselves\nbefore loading <code>ip.js</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">window<span class=\"token punctuation\">.</span>idio <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Proxy</span><span class=\"token punctuation\">(</span>\n  <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">{</span>\n    <span class=\"token function\">get</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">,</span> prop<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> prop <span class=\"token operator\">===</span> <span class=\"token string\">'r0'</span> <span class=\"token operator\">?</span> handleAjaxResponse <span class=\"token operator\">:</span> target<span class=\"token punctuation\">[</span>prop<span class=\"token punctuation\">]</span>\n\n      <span class=\"token comment\">// Alternatively, if the page can contain more than one Mustache template:</span>\n      <span class=\"token keyword\">return</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">^r\\d+$</span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">.</span><span class=\"token function\">test</span><span class=\"token punctuation\">(</span>prop<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">?</span> handleAjaxResponse <span class=\"token operator\">:</span> target<span class=\"token punctuation\">[</span>prop<span class=\"token punctuation\">]</span>\n      <span class=\"token comment\">// The `toString()` is needed because `prop` can be a Symbol,</span>\n      <span class=\"token comment\">// and the `test()` method would otherwise throw:</span>\n      <span class=\"token comment\">// \"Cannot convert a Symbol value to a string\"</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">handleAjaxResponse</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">data<span class=\"token punctuation\">,</span> statusCode</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// Do whatever with the data 🤘</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Here I'm setting <code>window.idio</code> to a proxy object which:</p>\n<ul>\n<li>Returns my own <code>handleAjaxResponse</code> function when <code>window.idio.r0</code> is accessed\n(instead of returning what <code>window.idio.r0</code> actually is).</li>\n<li>Otherwise makes <code>window.idio</code> work normally.</li>\n</ul>\n<p>In other words,\nI can do whatever I like in <code>handleAjaxResponse</code>.\nCheck the next section for an example what to do with the data.</p>\n<p>Heh, this is hacky and fragile,\nbut so is the Mustache + React combo.\nAt least now I don't have to have a mix of\nMustache, React and procedural DOM mutations.</p>\n<p>Goodbye Mustache!</p>\n<h3 id=\"sample-code\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#sample-code\">Sample code</a></h3>\n<p>I'll leave it as an exercise for you\nto clean up and properly comment the code. 🙂</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">ContentRecommendations</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> recommendations <span class=\"token operator\">=</span> <span class=\"token function\">useContentRecommendations</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token punctuation\">{</span><span class=\"token comment\">/*\n        Content recommendations are loaded\n        only if a Mustache template with this class is found on the page.\n      */</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>script</span>\n        <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>idio-recommendations<span class=\"token punctuation\">\"</span></span>\n        <span class=\"token attr-name\">data-api-key</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>...<span class=\"token punctuation\">\"</span></span>\n        <span class=\"token attr-name\">data-rpp</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>5<span class=\"token punctuation\">\"</span></span> <span class=\"token comment\">// Results per page</span>\n        <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>text/x-mustache<span class=\"token punctuation\">\"</span></span>\n      <span class=\"token punctuation\">/></span></span><span class=\"token plain-text\">\n\n      </span><span class=\"token punctuation\">{</span>recommendations<span class=\"token punctuation\">.</span>length <span class=\"token operator\">></span> <span class=\"token number\">0</span> <span class=\"token operator\">&amp;&amp;</span> <span class=\"token punctuation\">(</span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Recommended content</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">ContentRecommendationsList</span></span> <span class=\"token attr-name\">recommendations</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>recommendations<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span></span><span class=\"token punctuation\">></span></span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span></span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">ContentRecommendationsList</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> recommendations <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">formatDate</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">date</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n    <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span>date<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toLocaleString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">dateStyle</span><span class=\"token operator\">:</span> <span class=\"token string\">'medium'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> recommendations<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">recommendation</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">key</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>recommendation<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h3</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n        </span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* Look ma, can use router links! */</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">RouterLink</span></span> <span class=\"token attr-name\">href</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>recommendation<span class=\"token punctuation\">.</span>link_url<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n          </span><span class=\"token punctuation\">{</span>recommendation<span class=\"token punctuation\">.</span>title<span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span><span class=\"token class-name\">RouterLink</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h3</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>img</span> <span class=\"token attr-name\">alt</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span><span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">src</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>recommendation<span class=\"token punctuation\">.</span>main_image_url<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>recommendation<span class=\"token punctuation\">.</span>abstract<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n        </span><span class=\"token punctuation\">{</span><span class=\"token comment\">/* Look ma, date formatting! */</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n        Published on </span><span class=\"token punctuation\">{</span><span class=\"token function\">formatDate</span><span class=\"token punctuation\">(</span>recommendation<span class=\"token punctuation\">.</span>published<span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">/**\n * Get content recommendations loaded by `ip.js` (\"Idio Personalization\").\n *\n * Hacky! Read on for details.\n *\n * What `ip.js` does:\n *\n * 1. Look for a Mustache template with the class `idio-recommendations`.\n * 2. If found, load content recommendations data via Ajax.\n * 3. Call `window.idio.r0(data, statusCode)`\n *    with the Ajax response data and status code.\n *\n * Normally `window.idio.r0` compiles the Mustache template,\n * but `useIdioProxy` intercepts calls to `window.idio.r0`\n * so that we can handle the data ourselves.\n *\n * More detailed description and original code at\n * https://mtsknn.fi/blog/how-to-render-optimizely-content-recommendations-using-react/\n *\n * @returns {object[]}\n */</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">useContentRecommendations</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>recommendations<span class=\"token punctuation\">,</span> setRecommendations<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useIdioProxy</span><span class=\"token punctuation\">(</span>setRecommendations<span class=\"token punctuation\">)</span>\n  <span class=\"token function\">useIdioPersonalizationScript</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> recommendations\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">/**\n * Create a proxy object for `window.idio`\n * to intercept calls to `window.idio.r0`.\n *\n * Must be run before loading `ip.js`\n * to ensure that the proxy is in place\n * when loading the content recommendations data.\n *\n * `ip.js` sets `window.idio = window.idio || {}`,\n * so our proxy isn't overridden.\n *\n * @param {React.Dispatch&lt;React.SetStateAction&lt;object[]>>} setRecommendations\n * React state setter.\n */</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">useIdioProxy</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">setRecommendations</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> handleAjaxResponse <span class=\"token operator\">=</span> <span class=\"token function\">useCallback</span><span class=\"token punctuation\">(</span>\n    <span class=\"token punctuation\">(</span><span class=\"token parameter\">data<span class=\"token punctuation\">,</span> statusCode</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>statusCode <span class=\"token operator\">!==</span> <span class=\"token number\">200</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// Show error or do logging or whatever</span>\n        <span class=\"token keyword\">return</span>\n      <span class=\"token punctuation\">}</span>\n\n      <span class=\"token function\">setRecommendations</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">.</span>content<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">[</span>setRecommendations<span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    window<span class=\"token punctuation\">.</span>idio <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Proxy</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">get</span><span class=\"token punctuation\">(</span>target<span class=\"token punctuation\">,</span> property<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token keyword\">return</span> property <span class=\"token operator\">===</span> <span class=\"token string\">'r0'</span> <span class=\"token operator\">?</span> handleAjaxResponse <span class=\"token operator\">:</span> target<span class=\"token punctuation\">[</span>property<span class=\"token punctuation\">]</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token keyword\">delete</span> window<span class=\"token punctuation\">.</span>idio\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>handleAjaxResponse<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">/**\n * Load `ip.js` (\"Idio Personalization\")\n * which loads Optimizely Content Recommendations.\n */</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">useIdioPersonalizationScript</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> script <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'script'</span><span class=\"token punctuation\">)</span>\n    script<span class=\"token punctuation\">.</span>async <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span>\n    script<span class=\"token punctuation\">.</span>src <span class=\"token operator\">=</span> <span class=\"token string\">'https://.../ip.js'</span>\n\n    document\n      <span class=\"token punctuation\">.</span><span class=\"token function\">querySelector</span><span class=\"token punctuation\">(</span><span class=\"token string\">'script'</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">.</span><span class=\"token function\">insertAdjacentElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'beforebegin'</span><span class=\"token punctuation\">,</span> script<span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> script<span class=\"token punctuation\">.</span><span class=\"token function\">remove</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n","summary":"Also known as Episerver/Idio Content Recommendations.\nThe usual way of using a Mustache template\ncan be circumvented by using a JavaScript proxy.","date_published":"2021-06-24T12:00:00+03:00","date_modified":"2021-08-17T12:00:00+03:00","tags":["Episerver","JavaScript","React"]},{"id":"https://mtsknn.fi/blog/how-to-trigger-daily-netlify-builds-using-github-actions/","url":"https://mtsknn.fi/blog/how-to-trigger-daily-netlify-builds-using-github-actions/","title":"How to trigger daily Netlify builds using GitHub Actions","content_html":"<p>Or any other automatic periodic builds.\nI'm using daily builds to\nautomatically publish scheduled blog posts\non my Eleventy site.</p>\n<ol>\n<li>\n<p><a href=\"https://docs.netlify.com/configure-builds/build-hooks/\" class=\"link link-external\">Create a build hook on Netlify.</a>\nName it e.g. &quot;Daily build from GitHub Actions.&quot;</p>\n<p>&quot;Build hooks are URLs you can use to trigger new builds and deploys.&quot;\nTo trigger a build,\nyou can send a <code>POST</code> request to the URL.</p>\n</li>\n<li>\n<p><a href=\"https://docs.github.com/en/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository\" class=\"link link-external\">Create an encrypted secret for your GitHub repository.</a>\nName it e.g. <code>NETLIFY_DAILY_BUILD_HOOK</code>\nand use the URL from step 1 as the value.</p>\n<p>We are storing the URL as a secret\nbecause otherwise someone could spam the URL\nand rob you from your Netlify account's build minutes.</p>\n</li>\n<li>\n<p>Create a cron schedule expression using e.g. <a href=\"https://crontab.guru/\" class=\"link link-external\">Crontab.guru</a>\nor any of the other cron generators that can be easily found via Google.</p>\n<p>E.g. <code>50 5 * * *</code> means &quot;every day at 5:50 AM UTC.&quot;\n<a href=\"https://crontab.guru/examples.html\" class=\"link link-external\">Crontab.guru has more cron examples.</a></p>\n</li>\n<li>\n<p>Create a <code>.github/workflows/daily-build.yml</code> file with the following contents\n(replace the cron expression with your own):</p>\n<pre aria-label=\"Code block\" class=\"language-yaml bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> Daily Netlify build trigger\n\n<span class=\"token key atrule\">on</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">schedule</span><span class=\"token punctuation\">:</span>\n    <span class=\"token comment\"># Run at 5:50 AM UTC every day</span>\n    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">cron</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'50 5 * * *'</span>\n\n<span class=\"token key atrule\">jobs</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">build</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">runs-on</span><span class=\"token punctuation\">:</span> ubuntu<span class=\"token punctuation\">-</span>latest\n    <span class=\"token key atrule\">steps</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> Curl request to Netlify's build hook\n        <span class=\"token key atrule\">env</span><span class=\"token punctuation\">:</span>\n          <span class=\"token key atrule\">BUILD_HOOK</span><span class=\"token punctuation\">:</span> $<span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span> secrets.NETLIFY_DAILY_BUILD_HOOK <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span>\n        <span class=\"token key atrule\">run</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">|</span><span class=\"token scalar string\">\n          curl -X POST -d {} \"$BUILD_HOOK\"</span>\n</span></code></pre>\n</li>\n</ol>\n<p>And that's it!\nGitHub will automatically run the Action every day around the specified time.\nThe Action will do a POST request to the build hook\nwhich will trigger a build on Netlify.</p>\n<p>Note about <a href=\"https://docs.github.com/en/actions/reference/events-that-trigger-workflows#scheduled-events\" class=\"link link-external\">scheduled GitHub Actions events</a>:</p>\n<blockquote>\n<p>The <code>schedule</code> event can be delayed\nduring periods of high loads of GitHub Actions workflow runs.\nHigh load times include the start of every hour.\nTo decrease the chance of delay,\nschedule your workflow to run at a different time of the hour.</p>\n</blockquote>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<ul>\n<li><a href=\"https://docs.netlify.com/configure-builds/build-hooks/\" class=\"link link-external\">Build hooks on Netlify docs</a></li>\n<li><a href=\"https://docs.github.com/en/actions/reference/encrypted-secrets\" class=\"link link-external\">Encrypted repository secrets on GitHub docs</a></li>\n<li><a href=\"https://docs.github.com/en/actions/reference/events-that-trigger-workflows#scheduled-events\" class=\"link link-external\">Scheduled GitHub Actions events on GitHub docs</a></li>\n</ul>\n","summary":"Or any other automatic periodic builds.\nI'm using daily builds to\nautomatically publish scheduled blog posts\non my Eleventy site.","date_published":"2021-06-22T12:00:00+03:00","tags":["Eleventy","Git","Netlify"]},{"id":"https://mtsknn.fi/blog/how-to-use-absolute-import-paths-in-nodejs/","url":"https://mtsknn.fi/blog/how-to-use-absolute-import-paths-in-nodejs/","title":"Two ways to use absolute import paths in Node.js","content_html":"<p>Say goodbye to <code>../</code> times million\nand use absolute import paths from the app's root directory instead.\nTwo contenders: Basetag and Sultan's <code>sexy-require</code>! 🤺</p>\n<h2 id=\"sultans-sexy-require\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#sultans-sexy-require\">Sultan's <code>sexy-require</code></a></h2>\n<p>Example usage of the\n<a href=\"https://github.com/sultan99/sexy-require\" class=\"link link-external\"><code>sexy-require</code> npm package by Sultan</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Before, unclear what's being required 👎</span>\n<span class=\"token keyword\">const</span> user <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'../../../database/user'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// After, absolute path from the app's root dir 👍</span>\n<span class=\"token keyword\">const</span> user <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/database/user'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// Alternatively, using an alias configured in a `.paths` file 👍</span>\n<span class=\"token keyword\">const</span> user <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'$db/user'</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p><code>sexy-require</code> overrides <code>Module.prototype.require</code>,\nso in other files\n<code>require()</code> points to <code>sexy-require</code>'s <code>require</code> function\nwhich supports absolute paths and path aliases.</p>\n<h3 id=\"pros\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#pros\">Pros</a></h3>\n<ul>\n<li>Simple to set up:\ninstall the package\nand require it in your main application file.</li>\n<li>Supports path aliases.</li>\n<li>The name!</li>\n</ul>\n<h3 id=\"cons\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#cons\">Cons</a></h3>\n<ul>\n<li>Breaks VS Code's IntelliSense.</li>\n<li>Breaks path autocompletion in VS Code.</li>\n<li>Supports only <code>require()</code>, not <code>import</code>.</li>\n</ul>\n<h2 id=\"basetag\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#basetag\">Basetag</a></h2>\n<p>Example usage of the\n<a href=\"https://github.com/janniks/basetag\" class=\"link link-external\">Basetag npm package by Jannik</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Before, unclear what's being required 👎</span>\n<span class=\"token keyword\">import</span> user <span class=\"token keyword\">from</span> <span class=\"token string\">'../../../database/user'</span>\n<span class=\"token keyword\">const</span> user <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'../../../database/user'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// After, $ points to the app's root dir 👍</span>\n<span class=\"token keyword\">import</span> user <span class=\"token keyword\">from</span> <span class=\"token string\">'$/database/user'</span>\n<span class=\"token keyword\">const</span> user <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'$/database/user'</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Basetag creates a symlink,\nor something called a junction on Windows,\nso that <code>node_modules/$</code> points to the app's root directory.</p>\n<h3 id=\"pros-1\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#pros-1\">Pros</a></h3>\n<ul>\n<li>Works with VS Code's IntelliSense.</li>\n<li>Path autocompletion works in VS Code.</li>\n<li>Supports both <code>require()</code> and <code>import</code>.</li>\n</ul>\n<h3 id=\"cons-1\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#cons-1\">Cons</a></h3>\n<ul>\n<li>No way to configure path aliases.</li>\n<li>The symlink gets removed by npm when installing or removing packages,\nso you need to e.g. create a <code>postinstall</code> npm script\nas instructed in the package's readme.\nNot difficult,\nbut a little hassle.</li>\n</ul>\n<h2 id=\"which-one-to-use\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#which-one-to-use\">Which one to use</a></h2>\n<p>I'm not sure which one I like more:</p>\n<ul>\n<li>Better VS Code support of Basetag.</li>\n<li>The name <em>Sultan's <code>sexy-require</code></em>.</li>\n</ul>\n<p>Just kidding –\nuse <a href=\"https://github.com/janniks/basetag\" class=\"link link-external\">Basetag</a>,\ngood IDE support is more important.\nPlus it also supports <code>import</code> statements.</p>\n<h2 id=\"update-in-2023\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#update-in-2023\">Update in 2023</a></h2>\n<p>There are probably better solutions than these two.\nBesides, I wrote this post mainly because I found the name &quot;Sultan's <code>sexy-require</code>&quot; so sweet.</p>\n<p>Some time ago I came across a blog post saying that\n<a href=\"https://abhijithota.me/posts/node-import-aliases/\" class=\"link link-external\">Node.js supports import aliases natively since v14</a>.\nI haven't tested that, but it looks promising.</p>\n","summary":"Say goodbye to `../` times million\nand use absolute import paths from the app's root directory instead.\nTwo contenders: Basetag and Sultan's `sexy-require`! 🤺","date_published":"2021-06-17T12:00:00+03:00","date_modified":"2023-11-02T12:00:00+03:00","tags":["JavaScript","Node.js"]},{"id":"https://mtsknn.fi/blog/smoke-testing/","url":"https://mtsknn.fi/blog/smoke-testing/","title":"Smoke testing is quick and dirty testing","content_html":"<p>Smoke testing is cursory or &quot;quick and dirty&quot; testing\nto see that the software works correctly.</p>\n<h2 id=\"smoke-testing\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#smoke-testing\">Smoke testing</a></h2>\n<p>I recently heard the term &quot;smoke testing&quot;\nwhen we were talking about production deployments at work.\nLet's see\n<a href=\"https://en.wikipedia.org/wiki/Smoke_testing_(software)\" class=\"link link-external\">what Wikipedia tells us about <em>Smoke testing (software)</em></a>:</p>\n<blockquote>\n<p>In computer programming and software testing,\nsmoke testing\n(also confidence testing,\nsanity testing,\nbuild verification test (BVT)\nand build acceptance test)\nis preliminary testing to reveal simple failures severe enough to,\nfor example,\nreject a prospective software release.\nSmoke tests are a subset of test cases\nthat cover the most important functionality of a component or system,\nused to aid assessment of whether main functions of the software\nappear to work correctly.</p>\n</blockquote>\n<p>So: smoke testing is cursory or &quot;quick and dirty&quot; testing\nto see that the software works correctly.\nOr appears to work correctly –\nthere's no extensive testing.</p>\n<p>For example,\nif you do extensive testing in your integration or staging environment\n(or whatever your non-production test environment is called),\nthere might be no need to repeat the extensive tests in production.\nSo, during production deployments it's enough\nto verify that everything looks good at a cursory glance.\nThat's smoke testing.</p>\n<h3 id=\"sanity-testing\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#sanity-testing\">Sanity testing</a></h3>\n<p>The Wikipedia quote above\nsays that smoke testing is also called sanity testing.\nThis may or may not be true –\ncheck out\n<a href=\"https://stackoverflow.com/q/28605496/1079869\" class=\"link link-external\"><em>What is the difference between smoke testing and sanity testing?</em> on Stack Overflow</a>\nfor different opinions.</p>\n<h2 id=\"origin-of-the-term\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#origin-of-the-term\">Origin of the term</a></h2>\n<p>I was curious why smoke testing was called like that.\n<a href=\"https://en.wikipedia.org/wiki/Smoke_testing_(electrical)\" class=\"link link-external\">Wikipedia's page about <em>Smoke testing (electrical)</em></a>\nexplains it clearly:</p>\n<blockquote>\n<p>The expression probably was first used in plumbing\nin referring to tests for the detection of cracks, leaks or breaks\nin closed systems of pipes.\nBy metaphorical extension the term is used in electronics.\nIn <em>Lessons Learned in Software Testing</em>,\nCem Kaner, James Bach, and Brett Pettichord\nprovided the origin of the term:\n&quot;The phrase <em>smoke test</em> comes from electronic hardware testing.\nYou plug in a new board and turn on the power.\nIf you see smoke coming from the board, turn off the power.\nYou don't have to do any more testing.&quot;</p>\n</blockquote>\n<p>So: plumbing → electronic hardware testing → software testing.</p>\n","summary":"Smoke testing is cursory or \"quick and dirty\" testing\nto see that the software works correctly.","date_published":"2021-06-13T12:00:00+03:00","date_modified":"2023-02-28T12:00:00+03:00","tags":["Testing"]},{"id":"https://mtsknn.fi/blog/interdependent-react-prop-types-in-ts/","url":"https://mtsknn.fi/blog/interdependent-react-prop-types-in-ts/","title":"React + TypeScript: Interdependent prop types","content_html":"<p>I.e. props that depend on other props.\nYou can require and forbid certain prop combinations\nby utilizing TypeScript's union and intersection types.</p>\n<h2 id=\"example-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-problem\">Example problem</a></h2>\n<p>Let's say we want to create a <code>Shape</code> React component\nwith the following requirements:</p>\n<ul>\n<li>It takes a <code>color</code> prop.</li>\n<li>It takes a <code>type</code> prop\nwith the value of <code>'circle'</code>, <code>'square'</code> or <code>'rectangle'</code>.</li>\n<li>Depending on the <code>type</code> prop's value,\nit takes the following props as well:\n<ul>\n<li><code>radius</code> when the type is <code>'circle'</code>.</li>\n<li><code>width</code> when the type is <code>'square'</code>.</li>\n<li><code>width</code> and <code>height</code> when the type is <code>'rectangle'</code>.</li>\n</ul>\n</li>\n</ul>\n<p>A naive type for the props object would look like this:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">ShapeProps</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  color<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span>\n  type<span class=\"token operator\">:</span> <span class=\"token string\">'circle'</span> <span class=\"token operator\">|</span> <span class=\"token string\">'square'</span> <span class=\"token operator\">|</span> <span class=\"token string\">'rectangle'</span>\n  radius<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span>\n  width<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span>\n  height<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">Shape</span><span class=\"token punctuation\">(</span>props<span class=\"token operator\">:</span> ShapeProps<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token comment\">/* ... */</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>It's naive because we could specify <em>all</em> of those props:</p>\n<pre aria-label=\"Code block\" class=\"language-tsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">Shape</span></span> <span class=\"token attr-name\">color</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>red<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>circle<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">radius</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token number\">10</span><span class=\"token punctuation\">}</span></span> <span class=\"token attr-name\">width</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token number\">20</span><span class=\"token punctuation\">}</span></span> <span class=\"token attr-name\">height</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token number\">30</span><span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span>\n</span></code></pre>\n<p>That makes no sense because a circle shape doesn't have a width or a height;\nit only has a radius.</p>\n<p>The other shapes have similar problems.</p>\n<h2 id=\"union-type-to-the-rescue\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#union-type-to-the-rescue\">Union type to the rescue</a></h2>\n<p>We instead want to create a\n<a href=\"https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types\" class=\"link link-external\">union type</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">ShapeProps</span> <span class=\"token operator\">=</span>\n  <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span> color<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span> type<span class=\"token operator\">:</span> <span class=\"token string\">'circle'</span><span class=\"token punctuation\">;</span> radius<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span> color<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span> type<span class=\"token operator\">:</span> <span class=\"token string\">'square'</span><span class=\"token punctuation\">;</span> width<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span> color<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span><span class=\"token punctuation\">;</span> type<span class=\"token operator\">:</span> <span class=\"token string\">'rectangle'</span><span class=\"token punctuation\">;</span> width<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span> height<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Now TypeScript won't let us specify invalid props.</p>\n<p>For example, when the type is <code>'circle'</code>:</p>\n<ul>\n<li>We can (and must) specify only the <code>color</code> and <code>radius</code> props.</li>\n<li>We can't specify the <code>width</code> or <code>height</code> props;\ndoing so would produce an error.</li>\n</ul>\n<h2 id=\"simplify-with-an-intersection-type\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#simplify-with-an-intersection-type\">Simplify with an intersection type</a></h2>\n<p>Notice how we are repeating the <code>color</code> prop in all three types.</p>\n<p>We can reduce duplication by using an\n<a href=\"https://www.typescriptlang.org/docs/handbook/2/objects.html#intersection-types\" class=\"link link-external\">intersection type</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">type</span> <span class=\"token class-name\">ShapeProps</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span> color<span class=\"token operator\">:</span> <span class=\"token builtin\">string</span> <span class=\"token punctuation\">}</span> <span class=\"token operator\">&amp;</span> <span class=\"token punctuation\">(</span>\n  <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span> type<span class=\"token operator\">:</span> <span class=\"token string\">'circle'</span><span class=\"token punctuation\">;</span> radius<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span> type<span class=\"token operator\">:</span> <span class=\"token string\">'square'</span><span class=\"token punctuation\">;</span> width<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">|</span> <span class=\"token punctuation\">{</span> type<span class=\"token operator\">:</span> <span class=\"token string\">'rectangle'</span><span class=\"token punctuation\">;</span> width<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">;</span> height<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span> <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h2 id=\"more-tricks\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#more-tricks\">More tricks</a></h2>\n<p>I learned this trick from Bruno Antunes's\n<a href=\"https://www.youtube.com/watch?v=vXh4PFwZFGI\" class=\"link link-external\">YouTube video <em>React.js TypeScript Conditional Props – Props that depend on other Props</em> (26:13)</a>.\nCheck it out, it has more advanced examples as well.</p>\n","summary":"I.e. props that depend on other props.\nYou can require and forbid certain prop combinations\nby utilizing TypeScript's union and intersection types.","date_published":"2021-06-13T12:00:00+03:00","date_modified":"2023-03-01T12:00:00+03:00","tags":["JavaScript","React","TypeScript"]},{"id":"https://mtsknn.fi/blog/5-rules-of-aria/","url":"https://mtsknn.fi/blog/5-rules-of-aria/","title":"The 5 rules of ARIA","content_html":"<p>Five simple rules for using ARIA attributes,\nstraight from W3C.</p>\n<p><a href=\"https://www.w3.org/TR/using-aria/\" class=\"link link-external\">W3C's <em>Using ARIA</em> document</a>\nhas a <a href=\"https://www.w3.org/TR/using-aria/#notes2\" class=\"link link-external\"><em>Notes on ARIA Use in HTML</em> section</a>.</p>\n<p>It's a short section,\nand here's an even shorter summary:</p>\n<blockquote>\n<ol>\n<li>\n<p>If you <em>can</em> use a native HTML element or attribute [...] instead of [...] an ARIA role, state or property [...], <strong>then do so</strong>.</p>\n</li>\n<li>\n<p>Do not change native semantics, unless you really have to.</p>\n<p>For example: Developer wants to build a heading that's a tab.</p>\n<p>Do <strong>not</strong> do this:</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span> <span class=\"token attr-name\">role</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>tab<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>heading tab<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<p><strong>Do</strong> this:</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">role</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>tab<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>h2</span><span class=\"token punctuation\">></span></span>heading tab<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>h2</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n</li>\n<li>\n<p>All interactive ARIA controls must be usable with the keyboard.</p>\n</li>\n<li>\n<p>Do not use <code>role=&quot;presentation&quot;</code> or <code>aria-hidden=&quot;true&quot;</code> on a <strong>focusable</strong> element.</p>\n<p>Using either of these on a <strong>focusable</strong> element will result in some users focusing on 'nothing'.</p>\n</li>\n<li>\n<p>All interactive elements must have an accessible name.</p>\n</li>\n</ol>\n</blockquote>\n<p>Inspired by <a href=\"https://www.bram.us/2021/05/26/the-5-rules-of-aria/\" class=\"link link-external\">Bramus's summary of the five rules</a>.</p>\n","summary":"Five simple rules for using ARIA attributes,\nstraight from W3C.","date_published":"2021-06-13T12:00:00+03:00","date_modified":"2023-03-02T12:00:00+03:00","tags":["Accessibility"]},{"id":"https://mtsknn.fi/blog/goodbye-cookbook-hello-more-blog-posts/","url":"https://mtsknn.fi/blog/goodbye-cookbook-hello-more-blog-posts/","title":"Goodbye Cookbook, hello more blog posts","content_html":"<p>I ditched my website's Cookbook section\nand converted existing recipes to blog posts.\nMostly to reduce stress\nand make things clearer.</p>\n<p>Up until today,\nmy website had a Cookbook section\nwhich contained &quot;technical recipes&quot;:\nshort tips, tricks and code snippets.</p>\n<p>Today I converted all existing cookbook recipes to blog posts.</p>\n<p>I wasn't sure which articles should be cookbook recipes\nand which articles should be blog posts.\nSome blog posts already felt like they should be cookbook recipes,\nand vice versa.</p>\n<p>The cookbook just caused extra stress in the end\nbecause I had two things to maintain:\nthe blog and the cookbook.\nOr actually three things,\nsince <a href=\"/blog/bye-weekly-log/\" class=\"link\">I am (update: was) also writing a weekly log</a>.</p>\n<p>At some point I almost had the idea\nto make two versions of some articles:\na long and detailed blog post,\nand a short and straight-to-the-point cookbook recipe.</p>\n<p>But that would have been silly:\ndouble work for no apparent reason.\nIf a blog post is long,\nI'll rather include a short summary\nor split it into smaller posts.\nIf a blog post is short,\nthat's fine too!</p>\n<p>The &quot;downside&quot; of removing the cookbook\nis that I really like the terms &quot;cookbook&quot; and &quot;recipe.&quot;\nI think I was inspired by\n<a href=\"https://getkirby.com/docs/cookbook\" class=\"link link-external\">Kirby CMS's cookbook section</a>.</p>\n<p>Another source of inspiration was\n<a href=\"https://notes.zander.wtf/\" class=\"link link-external\">Zander Martineau's <em>Code Notes</em></a>.\nI found it a year ago via his blog post\n<a href=\"https://zander.wtf/blog/code-notes-release\" class=\"link link-external\"><em>Announcing Code Notes</em></a>.\nCool!\nI have now tried out something similar (though much simpler),\nand I'll rather keep my code notes under my blog.</p>\n<p>All right,\nnow I have more time and energy to write blog posts...\nor at least I hope so. 😅</p>\n","summary":"I ditched my website's Cookbook section\nand converted existing recipes to blog posts.\nMostly to reduce stress\nand make things clearer.","date_published":"2021-06-12T12:00:00+03:00","date_modified":"2023-03-17T12:00:00+03:00","tags":["Blogging"]},{"id":"https://mtsknn.fi/blog/my-favorite-mnemonics-for-american-vs-british-english/","url":"https://mtsknn.fi/blog/my-favorite-mnemonics-for-american-vs-british-english/","title":"My favorite mnemonics for American vs British English","content_html":"<p>Use these weird tricks to remember\nwhich words are American English\nand which are British English.\nLinguists HATE them!</p>\n<h2 id=\"learned-vs-learnt\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#learned-vs-learnt\">Learned vs learnt</a></h2>\n<p>&quot;Learned&quot; is American English,\n&quot;learnt&quot; is British English.</p>\n<p><strong>Mnemonic:</strong>\nEnglishmen like to drink <strong>tea</strong>,\nso the word ends in a <strong>t</strong>.</p>\n<p>Source of the mnemonic:\n<a href=\"https://www.grammarly.com/blog/learned-learnt/\" class=\"link link-external\"><em>Learned or Learnt?</em> on Grammarly</a></p>\n<h2 id=\"words-ending-in-er-vs-re\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#words-ending-in-er-vs-re\">Words ending in &quot;-er&quot; vs &quot;-re&quot;</a></h2>\n<p>Many words end in &quot;-er&quot; in American English\nand in &quot;-re&quot; in British English.</p>\n<p><strong>Examples:</strong></p>\n<table>\n<thead>\n<tr>\n<th>American English</th>\n<th>British English</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>center</td>\n<td>centre</td>\n</tr>\n<tr>\n<td>fiber</td>\n<td>fibre</td>\n</tr>\n<tr>\n<td>liter</td>\n<td>litre</td>\n</tr>\n<tr>\n<td>meter</td>\n<td>metre</td>\n</tr>\n<tr>\n<td>theater or theatre</td>\n<td>theatre</td>\n</tr>\n</tbody>\n</table>\n<blockquote>\n<p>Note that in American English,\n<em>theater</em> is the standard spelling,\nbut the <em>-re</em> spelling is also commonly used,\nespecially in proper names\n(<em>the Lunt-Fontanne Theatre</em>).</p>\n</blockquote>\n<p>Source of the rule and the quote:\n<a href=\"https://www.lexico.com/grammar/words-ending-in-re-er\" class=\"link link-external\"><em>Words ending in &quot;-re&quot; and &quot;-er&quot;</em> on Lexico</a></p>\n<p><strong>Mnemonic:</strong>\nin Am-<strong>er</strong>-ican English,\nmany words end in &quot;-<strong>er</strong>.&quot;</p>\n<p>Source of the mnemonic: me!</p>\n","summary":"Use these weird tricks to remember\nwhich words are American English\nand which are British English.\nLinguists HATE them!","date_published":"2021-06-11T12:00:00+03:00","tags":["English","Mnemonics"]},{"id":"https://mtsknn.fi/blog/squircley/","url":"https://mtsknn.fi/blog/squircley/","title":"Squircley is an SVG squircle generator","content_html":"<p>A squircle is a rounded square with no straight edges;\nthe shape is a continuous curve.</p>\n<p><a href=\"https://squircley.app/\" class=\"link link-external\">Squircley</a>\nis a simple tool for creating SVG &quot;squircles.&quot;</p>\n<p><img src=\"./squircley.png\" alt=\"Screenshot of the Squircley app, showing a yellow squircle and controls for customizing it.\"></p>\n<p>From the author's (George Francis)\ndev.to article <a href=\"https://dev.to/georgedoescode/codepen-soften-up-your-designs-with-a-squircle-3nd3\" class=\"link link-external\"><em>Soften up your designs with a Squircle!</em></a>:</p>\n<blockquote>\n<p>A Squircle,\nunlike an element with border-radius set,\nhas no straight edges at all.\nThe shape is a continuous curve!\nApple love this trick and use it in both physical and digital design.</p>\n</blockquote>\n<p>I <em>love</em> how squircles look! 🤩\nThough funnily <a href=\"https://hanki.dev/\" class=\"link link-external\">my brother</a> doesn't like them,\napparently because at some point\nthey have been popular in Android as well\nso he has already received an overdose of them. 🤔</p>\n<p>But I like them so\n<a href=\"https://squircley.app/\" class=\"link link-external\">check out Squircley</a>\nand <a href=\"https://dev.to/georgedoescode/codepen-soften-up-your-designs-with-a-squircle-3nd3\" class=\"link link-external\">see the dev.to article for an interactive example</a>!</p>\n","summary":"A squircle is a rounded square with no straight edges;\nthe shape is a continuous curve.","date_published":"2021-06-06T12:00:00+03:00","tags":["Cool tools","SVG"]},{"id":"https://mtsknn.fi/blog/kayaking/","url":"https://mtsknn.fi/blog/kayaking/","title":"Kayaking is fun","content_html":"<p>I kayaked for the first time in my life and quite liked it.\nThen I ate pizza.</p>\n<h2 id=\"kayaking\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#kayaking\">Kayaking</a></h2>\n<p>We went kayaking with our team on Tuesday.\nHere's a photo that I took\nusing my phone's (Unihertz Jelly) potato camera:</p>\n<p><img src=\"./kayaking-team.jpg\" alt=\"A photo taken from a red kayak which is partly showing in the bottom right corner. Eight other kayaks are in the background with people (my teammates) in them with their backs toward me. There are trees on the shores and buildings in the distant horizon.\"></p>\n<p>And here's a quick selfie that I snapped for my wife (nice picture 😂):</p>\n<p><img src=\"./kayaking-selfie.jpg\" alt=\"My selfie as I'm kayaking. Only my head is shown, I'm grinning widely with my teeth showing. Behind me is water and trees. My face is not focused properly which makes the photo silly.\"></p>\n<p>It was my first time kayaking.\nIt was quite fun but also surprisingly effortful\nas I had to use my legs to maintain balance\nand my arms and upper torso to move the kayak forward.</p>\n<p>I can definitely recommend trying out kayaking.</p>\n<h2 id=\"pizza\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#pizza\">Pizza</a></h2>\n<p>Afterwards we went eating.\nSome chose fancy foods like entrecôte,\nbut I chose an exotic pizza. 😋\nNo picture of it, sadly, but here are the ingredients:</p>\n<ul>\n<li>tomato sauce</li>\n<li>mozzarella cheese</li>\n<li>spreadable Nduja salami</li>\n<li>hot Spianata salami</li>\n<li>blood grapefruit marmalade</li>\n<li><a href=\"https://en.wikipedia.org/wiki/Eruca_vesicaria\" class=\"link link-external\">rocket/arugula</a></li>\n<li>parmesan cheese</li>\n<li>virgin olive oil.</li>\n</ul>\n<p>It wasn't very <em>delicious</em>,\nbut I wanted something exotic,\nand it was exotic. ¯\\(ツ)/¯</p>\n<p>I can definitely recommend trying out pizza too!</p>\n","summary":"I kayaked for the first time in my life and quite liked it.\nThen I ate pizza.","date_published":"2021-06-06T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/react-use-state-machine/","url":"https://mtsknn.fi/blog/react-use-state-machine/","title":"`useStateMachine`: &half;&nbsp;kB state machine hook for React","content_html":"<p>A state machine is a bit like <code>useReducer</code>,\nbut with greater constraints (in a good way).\nThis state machine hook is tiny.</p>\n<p>Cassio Zen's\n<a href=\"https://github.com/cassiozen/useStateMachine\" class=\"link link-external\"><code>useStateMachine</code> hook for React</a>\nis a tiny (less than half a kilobyte) package\nfor creating state machines.\nPair it with <a href=\"https://preactjs.com/\" class=\"link link-external\">Preact</a>,\nand you have a very small yet powerful bundle\nfor creating stateful UIs.</p>\n<p>If you are not familiar with state machines,\nCassio explains them in his\n<a href=\"https://www.youtube.com/watch?v=N0OaRdJuVlc\" class=\"link link-external\">YouTube video <em>Getting your act together with State Machines</em> (12:35)</a>.</p>\n<p>State machines are somewhat similar to reducers.\nFrom one comment by Cassio under the video:</p>\n<blockquote>\n<p>It's similar,\nbut with one important distinction:\nwith useReducer/redux you can always dispatch any action,\nand it will always reach the reducer.\nWith state machines,\nthe current state dictates which transitions you can &quot;dispatch&quot; next.</p>\n<p>And this small change has a big impact\nbecause it empowers you to create your own constraints in the code\nto make a more robust application.</p>\n</blockquote>\n<p>In the video,\nCassio uses\n<a href=\"https://github.com/davidkpiano/xstate\" class=\"link link-external\">a popular state machine library called XState</a>,\nbut the main idea is the same.\nSidenotes:</p>\n<ul>\n<li>XState is more featureful,\nbut also weighs more,\nabout 19 kB\n(including the <code>@xstate/react</code> package required by React).\nOther differences are mentioned in\n<a href=\"https://github.com/cassiozen/useStateMachine/wiki/XState-comparison\" class=\"link link-external\"><code>useStateMachine</code>'s Wiki page <em>XState comparison</em></a>.</li>\n<li>There's also\n<a href=\"https://github.com/davidkpiano/xstate/tree/main/packages/xstate-fsm\" class=\"link link-external\"><code>@xstate/fsm</code>, a 1 kB implementation of XState</a>.\nSome differences are mentioned in\n<a href=\"https://github.com/cassiozen/useStateMachine/issues/27\" class=\"link link-external\"><code>useStateMachine</code>'s issue #27</a>.</li>\n</ul>\n<p>Two more good videos by Cassio on the topic:</p>\n<ul>\n<li><a href=\"https://www.youtube.com/watch?v=jF1tO2hTdC0\" class=\"link link-external\"><em>Custom State Machine Hook with useReducer &amp; useEffect</em> (28:36)</a>\ngoes through implementing <code>useStateMachine</code> from scratch.\nThe latest version of the library is a bit more robust\n(and also a bit more cryptic since it's written in fancy TypeScript).</li>\n<li><a href=\"https://www.youtube.com/watch?v=uT-f6UJBqFE\" class=\"link link-external\"><em>React useStateMachine hook</em> (18:17)</a>\nshows two examples\nhow to use the <code>useStateMachine</code> hook.</li>\n</ul>\n","summary":"A state machine is a bit like `useReducer`,\nbut with greater constraints (in a good way).\nThis state machine hook is tiny.","date_published":"2021-05-30T12:00:00+03:00","tags":["JavaScript","React"]},{"id":"https://mtsknn.fi/blog/daily-stand-ups/","url":"https://mtsknn.fi/blog/daily-stand-ups/","title":"Sprint-centric vs people-centric daily stand-up meetings","content_html":"<p>A daily stand-up should focus on the sprint.\nBut it should also be about the team members.</p>\n<p>In January,\nI read Critter's (Mike Crittenden) blog post\n<a href=\"https://critter.blog/2020/08/17/zombie-standups-try-walking-the-board/\" class=\"link link-external\"><em>Zombie standups? Try walking the board</em></a>.\nSince then,\nwe have been practicing <em>sprint-centric</em> daily stand-ups in our team.\nInstead of going person by person,\nwe go ticket by ticket.</p>\n<p>I think it was a step in the right direction.\nPreviously our daily stand-ups were more chaotic,\noften not focusing on the sprint enough.</p>\n<p>But there's a but.\nThe downside is that there's no good opportunity to discuss ad hoc things.\nCritter does cover this in his blog post:</p>\n<blockquote>\n<p><em>But what about general announcements the team needs to hear?</em>\nAfter you walk the board,\nask &quot;any general things we need to discuss?&quot; and let people speak up.</p>\n</blockquote>\n<p>...but that hasn't worked well.\nMaybe opening your mouth at the end is just too difficult.\nFor example, some people are shy.</p>\n<p>This week we changed back to <em>people-centric</em> daily stand-ups.\nTo help us keep the main focus on the sprint and its goals,\nI created quick filters in Jira for each person.\nFor example,\nwhen it's my turn to speak,\nI activate the &quot;Matias&quot; quick filter\nso that only tasks assigned to me\nare showing on the board.\nThen it's easy for me to talk about my tasks\n<em>plus</em> state any other things I have on my mind\nbecause I'm already speaking.</p>\n<p>The upside so far has been that there has been more ad hoc discussion,\nmeaning more sharing of information relevant to the team.</p>\n<p>We'll see in the coming weeks\nwhether this &quot;new old&quot; way of doing daily stand-ups is better or worse.</p>\n","summary":"A daily stand-up should focus on the sprint.\nBut it should also be about the team members.","date_published":"2021-05-30T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/cursive-parinfer/","url":"https://mtsknn.fi/blog/cursive-parinfer/","title":"Cursive (Clojure IDE) supports Parinfer out of the box","content_html":"<p>It's not mentioned in the docs or anywhere,\nbut you can enable Parinfer from Cursive's settings.\nWorks great with IdeaVim.</p>\n<h2 id=\"paredit-bad-for-me\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#paredit-bad-for-me\">Paredit bad (for me)</a></h2>\n<p><a href=\"https://cursive-ide.com/\" class=\"link link-external\">Cursive, the IDE for Clojure and ClojureScript</a>,\nuses <a href=\"https://cursive-ide.com/userguide/paredit.html\" class=\"link link-external\">Paredit for structural editing</a>.</p>\n<p>Editing code in Cursive has been a hassle\nbecause I'm using IdeaVim (a Vim plugin for Cursive and other IntelliJ-based IDEs)\nand haven't configured keybindings for Paredit.\nI haven't found any good tutorials on that,\nso it feels overwhelming.\n(Otherwise I have liked Cursive when doing\nClojure exercises on <a href=\"https://exercism.org/\" class=\"link link-external\">Exercism</a>.)</p>\n<h2 id=\"parinfer-good-and-how-to-enable-it-in-cursive\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#parinfer-good-and-how-to-enable-it-in-cursive\">Parinfer good (and how to enable it in Cursive)</a></h2>\n<p>I have dreamed of using <a href=\"https://shaunlebron.github.io/parinfer/\" class=\"link link-external\">Parinfer</a> in Cursive.\nParinfer seems so much easier to use than Paredit.</p>\n<p>Today my dreams came true\nbecause turns out that Cursive supports Parinfer out of the box!\n<strong>Go to <em>Settings → Editor → General → Smart Keys → Clojure</em>\nand choose &quot;Parinfer Smart mode&quot; from the &quot;Structural editing style&quot; dropdown.</strong></p>\n<h2 id=\"rant\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#rant\">Rant</a></h2>\n<p>What's infuriating is that\nthere's <em>zero</em> mention of Parinfer in the Cursive docs\nor in Cursive's GitHub Wiki. 🤦‍♂️\nI have wasted so much time trying to google &quot;cursive ide parinfer&quot;\nand finding only a few mentions that &quot;Cursive supports Parinfer&quot;\nbut no information how to enable that.\nI found the setting today by accident\nbecause I was just going to disable Paredit.</p>\n","summary":"It's not mentioned in the docs or anywhere,\nbut you can enable Parinfer from Cursive's settings.\nWorks great with IdeaVim.","date_published":"2021-05-30T12:00:00+03:00","tags":["Clojure"]},{"id":"https://mtsknn.fi/blog/react-router-scroll-to-top/","url":"https://mtsknn.fi/blog/react-router-scroll-to-top/","title":"Scrolling to top on page changes in React Router (v5)","content_html":"<p>A common solution is to scroll to top every time the page changes.\nThat's incorrect:\nthe page should be scrolled to top only when navigating to <em>new</em> pages,\nnot when navigating back or forward.</p>\n<h2 id=\"common-but-wrong-solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#common-but-wrong-solution\">Common but wrong solution</a></h2>\n<p>When navigating in a Single Page Application (SPA),\nyou have to handle scrolling manually.\nHere's a <a href=\"https://stackoverflow.com/q/36904185/1079869\" class=\"link link-external\">solution that's often recommended</a>,\nbut this is wrong:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> useLocation <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react-router-dom'</span>\n\n<span class=\"token comment\">// Use this hook in some top-level component</span>\n<span class=\"token comment\">// which is inside a `&lt;Router>` component</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">useOnPageChange</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> pathname <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">useLocation</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    window<span class=\"token punctuation\">.</span><span class=\"token function\">scrollTo</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>pathname<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>It's wrong because\nthe page is scrolled to top on all page changes –\neven when using the browser's back and forward navigation buttons.</p>\n<p>Compare with any non-SPA site,\nlike my website or <a href=\"https://en.wikipedia.org/\" class=\"link link-external\">Wikipedia</a>.\nWhen using the browser's back/forward buttons,\nthe previous/next page's scrolling position should be restored.\n<strong>The page should be scrolled to top only when navigating to <em>new</em> pages.</strong></p>\n<h2 id=\"correct-solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#correct-solution\">Correct solution</a></h2>\n<p>The fix is to check the value of the\n<a href=\"https://github.com/ReactTraining/history/blob/v5.0.0/docs/api-reference.md#action\" class=\"link link-external\"><code>history</code> object's <code>action</code> parameter</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> useHistory<span class=\"token punctuation\">,</span> useLocation <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react-router-dom'</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">useOnPageChange</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> action <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">useHistory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> pathname <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">useLocation</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// Do nothing when using the browser's back/forward buttons</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>action <span class=\"token operator\">===</span> <span class=\"token string\">'POP'</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span>\n\n    window<span class=\"token punctuation\">.</span><span class=\"token function\">scrollTo</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>action<span class=\"token punctuation\">,</span> pathname<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>You could (and probably should) also do other things in the hook,\nlike focus the main heading of the new page.</p>\n","summary":"A common solution is to scroll to top every time the page changes.\nThat's incorrect:\nthe page should be scrolled to top only when navigating to _new_ pages,\nnot when navigating back or forward.","date_published":"2021-05-23T12:00:00+03:00","tags":["JavaScript","React"]},{"id":"https://mtsknn.fi/blog/emphasis-as-progressive-enhancement/","url":"https://mtsknn.fi/blog/emphasis-as-progressive-enhancement/","title":"`<strong>` and `<em>` should be treated as progressive enhancement","content_html":"<p>Screen readers don't announce emphasized text,\nso your text should be understandable even without the emphases.</p>\n<p>Martin Underhill writes that\n<a href=\"https://www.tempertemper.net/blog/bold-and-italics-arent-read-by-screen-readers\" class=\"link link-external\">bold and italics aren't read by screen readers</a>.</p>\n<blockquote>\n<p>Interestingly,\nthe NVDA screen reader added support for emphasis [in 2015],\nonly to remove it following complaints by users.</p>\n</blockquote>\n<p>I had noticed before\nthat screen readers indeed don't announce\nif the currently read text is bold or italics.</p>\n<p>But I hadn't considered this mindset:</p>\n<blockquote>\n<p>Text-level semantics like italics and bold\nshould be treated as a progressive enhancement.\nIn other words,\nyour sentences should make sense without emphasis;\nthose <code>&lt;em&gt;</code> and <code>&lt;strong&gt;</code> wrappers\nshould just offer a nice added extra\nfor [sighted users].</p>\n</blockquote>\n<p>Makes sense.</p>\n<p>Strikethrough is a similar but more difficult case\nbecause it <em>adds</em> content.\nRead <a href=\"https://www.tempertemper.net/blog/be-careful-with-strikethrough\" class=\"link link-external\">Martin's another post <em>Be careful with strikethrough</em></a>\nfor more details.\nThe post contains several good points\nwhy strikethrough is better avoided entirely.</p>\n","summary":"Screen readers don't announce emphasized text,\nso your text should be understandable even without the emphases.","date_published":"2021-05-23T12:00:00+03:00","tags":["Accessibility"]},{"id":"https://mtsknn.fi/blog/delegate-types-in-csharp-action-vs-func-vs-predicate/","url":"https://mtsknn.fi/blog/delegate-types-in-csharp-action-vs-func-vs-predicate/","title":"Delegate types in C#: `Action` vs `Func` vs `Predicate`","content_html":"<p><code>Action</code> doesn't return anything,\n<code>Func</code> does.\n<code>Predicate</code> takes one parameter and returns a boolean.</p>\n<h2 id=\"actionlesst1-t2-t3-greater\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#actionlesst1-t2-t3-greater\"><code>Action&lt;T1, T2, T3, ...&gt;</code></a></h2>\n<ul>\n<li><code>Action</code> has no return value.</li>\n<li>The types <code>T1</code>, <code>T2</code>, <code>T3</code> and so on specify the parameters' types.</li>\n<li>There can be between 0 and 16 parameters:\n<ul>\n<li><code>Action</code></li>\n<li><code>Action&lt;T1&gt;</code></li>\n<li><code>Action&lt;T1, T2&gt;</code></li>\n<li><code>Action&lt;T1, T2, T3&gt;</code></li>\n<li>...</li>\n<li><code>Action&lt;T1, T2, T3, ..., T16&gt;</code></li>\n</ul>\n</li>\n</ul>\n<p>Examples:</p>\n<ul>\n<li><code>Action</code> takes no parameters and returns nothing.</li>\n<li><code>Action&lt;int&gt;</code> takes one <code>int</code> parameter and returns nothing.</li>\n<li><code>Action&lt;int, bool, int&gt;</code> takes three parameters\nof types <code>int</code>, <code>bool</code> and <code>int</code>,\nin that order,\nand returns nothing.</li>\n</ul>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token class-name\">Action</span> lambda1 <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token punctuation\">{</span>\n    Console<span class=\"token punctuation\">.</span><span class=\"token function\">WriteLine</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"lambda1 was called\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// No return value</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token class-name\">Action<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">int</span><span class=\"token punctuation\">></span></span> lambda2 <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\"><span class=\"token keyword\">int</span></span> param1<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token punctuation\">{</span>\n    Console<span class=\"token punctuation\">.</span><span class=\"token function\">WriteLine</span><span class=\"token punctuation\">(</span><span class=\"token interpolation-string\"><span class=\"token string\">$\"lambda2 was called with </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span><span class=\"token expression language-csharp\">param1</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// No return value</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token class-name\">Action<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">int</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">bool</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">int</span><span class=\"token punctuation\">></span></span> lambda3 <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\"><span class=\"token keyword\">int</span></span> param1<span class=\"token punctuation\">,</span> <span class=\"token class-name\"><span class=\"token keyword\">bool</span></span> param2<span class=\"token punctuation\">,</span> <span class=\"token class-name\"><span class=\"token keyword\">int</span></span> param3<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token punctuation\">{</span>\n    Console<span class=\"token punctuation\">.</span><span class=\"token function\">WriteLine</span><span class=\"token punctuation\">(</span><span class=\"token interpolation-string\"><span class=\"token string\">$\"lambda3 was called with </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span><span class=\"token expression language-csharp\">param1</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">, </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span><span class=\"token expression language-csharp\">param2</span><span class=\"token punctuation\">}</span></span><span class=\"token string\"> and </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span><span class=\"token expression language-csharp\">param3</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// No return value</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n<h2 id=\"funclesst1-t2-t3-tresultgreater\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#funclesst1-t2-t3-tresultgreater\"><code>Func&lt;T1, T2, T3, ..., TResult&gt;</code></a></h2>\n<ul>\n<li><code>Func</code> returns a value of type <code>TResult</code>.</li>\n<li>The types <code>T1</code>, <code>T2</code>, <code>T3</code> and so on specify the parameters' types.</li>\n<li>There can be between 0 and 16 parameters:\n<ul>\n<li><code>Func&lt;TResult&gt;</code></li>\n<li><code>Func&lt;T1, TResult&gt;</code></li>\n<li><code>Func&lt;T1, T2, TResult&gt;</code></li>\n<li><code>Func&lt;T1, T2, T3, TResult&gt;</code></li>\n<li>...</li>\n<li><code>Func&lt;T1, T2, T3, ..., T16, TResult&gt;</code></li>\n</ul>\n</li>\n<li><code>TResult</code> is always in the last position,\ni.e. the last type always specifies the return value's type.</li>\n</ul>\n<p>Examples:</p>\n<ul>\n<li><code>Func&lt;string&gt;</code> takes no parameters and returns a <code>string</code>.</li>\n<li><code>Func&lt;int, string&gt;</code> takes one <code>int</code> parameter and returns a <code>string</code>.</li>\n<li><code>Func&lt;int, bool, int, string&gt;</code> takes three parameters\nof types <code>int</code>, <code>bool</code> and <code>int</code>,\nin that order,\nand returns a <code>string</code>.</li>\n</ul>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token class-name\">Func<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">string</span><span class=\"token punctuation\">></span></span> lambda1 <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token string\">\"lambda1 was called\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token class-name\">Func<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">int</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">string</span><span class=\"token punctuation\">></span></span> lambda2 <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\"><span class=\"token keyword\">int</span></span> param1<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token interpolation-string\"><span class=\"token string\">$\"lambda2 was called with </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span><span class=\"token expression language-csharp\">param1</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token class-name\">Func<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">int</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">bool</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">int</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">string</span><span class=\"token punctuation\">></span></span> lambda3 <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\"><span class=\"token keyword\">int</span></span> param1<span class=\"token punctuation\">,</span> <span class=\"token class-name\"><span class=\"token keyword\">bool</span></span> param2<span class=\"token punctuation\">,</span> <span class=\"token class-name\"><span class=\"token keyword\">int</span></span> param3<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// `null` return value is ok too</span>\n    <span class=\"token comment\">// because strings can be null</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>param2<span class=\"token punctuation\">)</span> <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token interpolation-string\"><span class=\"token string\">$\"lambda3 was called with </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span><span class=\"token expression language-csharp\">param1</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">, </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span><span class=\"token expression language-csharp\">param2</span><span class=\"token punctuation\">}</span></span><span class=\"token string\"> and </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span><span class=\"token expression language-csharp\">param3</span><span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n<h2 id=\"predicatelesstgreater\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#predicatelesstgreater\"><code>Predicate&lt;T&gt;</code></a></h2>\n<ul>\n<li><code>Predicate</code> returns a boolean.</li>\n<li><code>Predicate</code> takes one parameter of type <code>T</code>.</li>\n</ul>\n<p>Examples:</p>\n<ul>\n<li><code>Predicate&lt;int&gt;</code> takes one <code>int</code> parameter and returns a boolean.</li>\n<li><code>Predicate&lt;string&gt;</code> takes one <code>string</code> parameter and returns a boolean.</li>\n</ul>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token class-name\">Predicate<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">int</span><span class=\"token punctuation\">></span></span> IsPositive <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\"><span class=\"token keyword\">int</span></span> param<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> param <span class=\"token operator\">></span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token class-name\">Predicate<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">string</span><span class=\"token punctuation\">></span></span> StartsWithFoo <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\"><span class=\"token keyword\">string</span></span> param<span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> param<span class=\"token punctuation\">?.</span><span class=\"token function\">StartsWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"foo\"</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">??</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n<h2 id=\"predicatelesstgreater-vs-funclesst-boolgreater\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#predicatelesstgreater-vs-funclesst-boolgreater\"><code>Predicate&lt;T&gt;</code> vs <code>Func&lt;T, bool&gt;</code></a></h2>\n<p><code>Predicate&lt;T&gt;</code> is effectively the same as <code>Func&lt;T, bool&gt;</code>.\nThey both take a single parameter of type <code>T</code>\nand return a boolean.</p>\n<p>That said,\n<a href=\"https://stackoverflow.com/a/665572/1079869\" class=\"link link-external\">they are not assignment-compatible</a>.\nExample from the linked Stack Overflow answer by Daniel Earwicker\n(slightly modified):</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">public</span> <span class=\"token keyword\">static</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">bool</span></span> <span class=\"token function\">IsNegative</span><span class=\"token punctuation\">(</span><span class=\"token class-name\"><span class=\"token keyword\">int</span></span> x<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> x <span class=\"token operator\">&lt;</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">static</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Main</span><span class=\"token punctuation\">(</span><span class=\"token class-name\"><span class=\"token keyword\">string</span><span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span></span> args<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token class-name\">Predicate<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">int</span><span class=\"token punctuation\">></span></span> p <span class=\"token operator\">=</span> IsNegative<span class=\"token punctuation\">;</span>\n    <span class=\"token class-name\">Func<span class=\"token punctuation\">&lt;</span><span class=\"token keyword\">int</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">bool</span><span class=\"token punctuation\">></span></span> f <span class=\"token operator\">=</span> IsNegative<span class=\"token punctuation\">;</span>\n\n    p <span class=\"token operator\">=</span> f<span class=\"token punctuation\">;</span> <span class=\"token comment\">// Doesn't work</span>\n    f <span class=\"token operator\">=</span> p<span class=\"token punctuation\">;</span> <span class=\"token comment\">// Doesn't work</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Which one to use,\n<code>Predicate&lt;T&gt;</code> or <code>Func&lt;T, bool&gt;</code>?</p>\n<p>If you can choose,\nmy hunch is that <code>Func</code> is better because:</p>\n<ul>\n<li><code>Func</code> has overloads that take more than one input type,\nso it's more flexible than <code>Predicate</code>.</li>\n<li><code>Func&lt;T, bool&gt;</code> is clear (given that you know how <code>Func</code>s work),\nwhereas <code>Predicate</code> is more niche\nand makes sense only if you know what a &quot;predicate&quot; is.</li>\n</ul>\n<p>On the other hand,\n<code>Predicate&lt;T&gt;</code> is short and descriptive,\nso I don't have a strong opinion\nwhich one to prefer.</p>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<p>This blog post was inspired by\nScott Steffes's\n<a href=\"https://www.youtube.com/watch?v=E-g8Ivss0Gs\" class=\"link link-external\">YouTube video <em>Generic Delegate Types (Action, Func, Predicate), in C#</em></a>.\nThere's interesting discussion about the delegate keyword versus lambda syntax\nin <a href=\"https://old.reddit.com/r/csharp/comments/lzmn7y/whenever_i_need_to_pass_a_function_into_another/\" class=\"link link-external\">a Reddit thread around the video</a>.</p>\n<p>See also these pages in the .NET docs:</p>\n<ul>\n<li><a href=\"https://docs.microsoft.com/en-us/dotnet/api/system.action?view=net-5.0\" class=\"link link-external\"><code>Action</code> Delegate</a></li>\n<li><a href=\"https://docs.microsoft.com/en-us/dotnet/api/system.func-1?view=net-5.0\" class=\"link link-external\"><code>Func&lt;TResult&gt;</code> Delegate</a></li>\n<li><a href=\"https://docs.microsoft.com/en-us/dotnet/api/system.predicate-1?view=net-5.0\" class=\"link link-external\"><code>Predicate&lt;T&gt;</code> Delegate</a></li>\n</ul>\n","summary":"`Action` doesn't return anything,\n`Func` does.\n`Predicate` takes one parameter and returns a boolean.","date_published":"2021-05-22T12:00:00+03:00","date_modified":"2021-06-11T12:00:00+03:00","tags":["C#"]},{"id":"https://mtsknn.fi/blog/comment-terminal-commands/","url":"https://mtsknn.fi/blog/comment-terminal-commands/","title":"Comment terminal commands to find them later more easily","content_html":"<p>If you append a comment (like <code># foo</code>) to a terminal command,\nyou can search by the comment text when using <kbd>Ctrl</kbd>+<kbd>R</kbd>.</p>\n<p>Full example:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">curl</span> http://1.2.3.4/linux.iso  <span class=\"token comment\"># Fred's distribution of Linux</span>\n</span></code></pre>\n<p>Later you can press <kbd>Ctrl</kbd>+<kbd>R</kbd>\nand search for e.g. &quot;Fred&quot; to find the command.</p>\n<p>The example is from\nclimagic's <a href=\"https://www.youtube.com/watch?v=UvZY7bYt2Lo\" class=\"link link-external\">YouTube video <em>Using comments in the interactive shell</em> (3:32)</a>.</p>\n","summary":"If you append a comment (like `# foo`) to a terminal command,\nyou can search by the comment text when using Ctrl + R.","date_published":"2021-05-15T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/4-1-wrapping-styles-for-markdown-prose-and-code-comments/","url":"https://mtsknn.fi/blog/4-1-wrapping-styles-for-markdown-prose-and-code-comments/","title":"4 + 1 wrapping styles for Markdown prose and code comments","content_html":"<p>A paragraph per line,\na sentence per line,\nor hard wrapping at e.g. 80 characters?\nThese days I prefer a fourth option:\nsemantic line breaks.</p>\n<h2 id=\"context\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#context\">Context</a></h2>\n<p>For a long time\nI used to hard wrap my Markdown prose and code comments at 80 characters.\nIt looked quite neat.</p>\n<p>When a certain colleague touched the same files,\nhe always changed the wrapping style to a sentence per line.\nNot necessarily for the whole documents,\nbut for the parts that he had modified,\ne.g. a section in a Markdown file.</p>\n<p>I didn't like it at first.\nMy beautiful hard wrapped prose!\nI was too shy to complain about it,\nand it wasn't actually that big of a deal.</p>\n<p>Eventually I started to like the new style.\nNow I think it was a step in the right direction.</p>\n<h2 id=\"benefits-of-a-consistent-wrapping-style\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#benefits-of-a-consistent-wrapping-style\">Benefits of a consistent wrapping style</a></h2>\n<p>Why does it matter\nhow you wrap Markdown prose and code comments?\nWhile pondering over something like this\nmay seem superficial,\na good wrapping style does provide benefits:</p>\n<ul>\n<li>\n<p><strong>Readability.</strong></p>\n<p>Code is read more often than it's written,\nso readability is important.\nReadability also helps with writing and editing longer pieces of text.</p>\n</li>\n<li>\n<p><strong>Maintainability.</strong></p>\n<p>For example:\nif you hard wrap at 80 characters,\nanyone editing the text has to manually update and tweak the wrapping,\nwhich can be annoying.\nOr you can use a tool,\nbut finding a suitable tool and installing and configuring it\nis one more thing to worry about.</p>\n</li>\n<li>\n<p><strong>Diffability (version controlling).</strong></p>\n<p>For example:\nif you have a long paragraph on a single line,\nchanging one word highlights the whole paragraph\nin a Git diff.\nIf you instead have a single sentence\nor even half a sentence\non a single line,\nchanging one word highlights just that single sentence / half a sentence.</p>\n</li>\n</ul>\n<h2 id=\"not-limited-to-markdown\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#not-limited-to-markdown\">Not limited to Markdown</a></h2>\n<p>So far I have talked about Markdown (and code comments),\nbut these wrapping styles can be applied\nto many other lightweight markup languages as well.\nHere's a non-exhaustive list of\nother compatible lightweight markup languages:</p>\n<ul>\n<li><a href=\"https://asciidoc.org/\" class=\"link link-external\">AsciiDoc</a>,\n&quot;a text document format for writing notes, documentation, articles, books,\nebooks, slideshows, web pages, man pages and blogs.&quot;</li>\n<li><a href=\"https://orgmode.org/\" class=\"link link-external\">Org mode</a>,\n&quot;a GNU Emacs major mode for convenient plain text markup –\nand much more.&quot;</li>\n<li><a href=\"https://docutils.sourceforge.io/rst.html\" class=\"link link-external\">reStructuredText</a>,\n&quot;an easy-to-read, what-you-see-is-what-you-get plaintext markup syntax\nand parser system.&quot;</li>\n<li><a href=\"https://en.wikipedia.org/wiki/Help:Wikitext\" class=\"link link-external\">Wikitext</a>,\n&quot;consists of the syntax and keywords\nused by the MediaWiki software to format a page.&quot;\nThe best known MediaWiki site (or &quot;software&quot;) is Wikipedia.</li>\n</ul>\n<p>The above list is adapted from a similar list on the website of\n<a href=\"https://sembr.org/\" class=\"link link-external\">Semantic Line Breaks specification</a>.\nThe descriptions are copied from the linked pages.</p>\n<h2 id=\"the-4-1-wrapping-styles\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-4-1-wrapping-styles\">The 4 + 1 wrapping styles</a></h2>\n<p>Let's use the following examples\nfor comparing the different wrapping styles:</p>\n<ol>\n<li>\n<p>The following paragraph from my blog post\n<a href=\"https://mtsknn.fi/blog/switch-statements-default-doesnt-have-to-be-the-last-case/\" class=\"link\"><em>Switch statements: <code>default</code> doesn't have to be the last case</em></a>:</p>\n<blockquote>\n<p>After some thought,\nit's actually logical to present the game states (<code>switch</code> cases)\nin the order they have been presented:\n&quot;waiting to start,&quot;\n&quot;playing&quot;\nand &quot;dead.&quot;\nThat's the natural flow of the game states.\nThe initial game state is &quot;waiting to start,&quot;\nso the most logical position for the <code>default</code> case\nis at the beginning.</p>\n</blockquote>\n</li>\n<li>\n<p>The following code comment from my\n<a href=\"https://github.com/mtsknn/AutoHotkey/blob/b39db08/scripts/special_characters.ahk\" class=\"link link-external\">AutoHotkey script <code>special_characters.ahk</code></a>:</p>\n<blockquote>\n<p>No need to define separate hotstrings for uppercase letters\nbecause hotstrings are &quot;case-conforming&quot; by default.\nApplies to the <code>;aring</code> → <code>å</code> hotstring as well.</p>\n</blockquote>\n</li>\n</ol>\n<p>The Markdown paragraph is rendered in the same way with all wrapping styles.\nThe difference is in how the text looks like in the source code.</p>\n<p>(Code comments are often read only in the source code,\nso the above doesn't apply as well.)</p>\n<h3 id=\"a-paragraph-per-line\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#a-paragraph-per-line\">A paragraph per line</a></h3>\n<h4>Readability</h4>\n<p>Possibly annoying to read\nbecause lines are so long.\nDepends on the window width of the IDE\nand whether long lines are hard wrapped automatically.</p>\n<h4>Maintainability</h4>\n<p>Paragraphs are easy to identify,\nso the style is easy to follow.</p>\n<p>On the other hand,\nediting long lines is cumbersome\nas you can't e.g. rearrange the sentences of a paragraph by rearranging lines.</p>\n<h4>Diffability</h4>\n<p>Git diffs are noisy,\nespecially for long paragraphs,\nbecause changing a single word highlights the whole paragraph.\nIt looks like the whole paragraph has been touched,\nand it kind of has because it's on a single line,\nso you have to manually check what has actually changed.</p>\n<h4>Markdown example</h4>\n<p>The line is very long (329 characters),\ncausing a horizontal scroll bar to appear.</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">After some thought, it's actually logical to present the game states (<span class=\"token code-snippet code keyword\">`switch`</span> cases) in the order they have been presented: \"waiting to start,\" \"playing\" and \"dead.\" That's the natural flow of the game states. The initial game state is \"waiting to start,\" so the most logical position for the <span class=\"token code-snippet code keyword\">`default`</span> case is at the beginning.\n</span></code></pre>\n<h4>Code comment example</h4>\n<p>Same here:\na wild horizontal scroll bar appears!</p>\n<pre aria-label=\"Code block\" class=\"language-clojure bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">; No need to define separate hotstrings for uppercase letters because hotstrings are \"case-conforming\" by default. Applies to the `;aring` → `å` hotstring as well.</span>\n</span></code></pre>\n<h3 id=\"a-sentence-per-line\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#a-sentence-per-line\">A sentence per line</a></h3>\n<h4>Readability</h4>\n<p>Better than in the &quot;a paragraph per line&quot; style.\nThe problem is that a sentence can contain multiple points and ideas,\nso you have to check the whole line to see what's it about.</p>\n<p>Long sentences might be annoying to read\nif they are not automatically hard wrapped by the IDE.</p>\n<h4>Maintainability</h4>\n<p>Sentences are easy to identify,\nso the style is easy to follow.</p>\n<p>Sentences can be easily rearranged\nby rearranging lines.</p>\n<p>Might encourage not-too-long sentences\nbecause very long lines stand out.</p>\n<h4>Diffability</h4>\n<p>Better than in the &quot;a paragraph per line&quot; style.\nSome lines might still be too long,\nmaking diffs noisy.</p>\n<h4>Markdown example</h4>\n<p>I like this better than putting the whole paragraph on the same line.\nThe first and last lines are still too long, though.</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">After some thought, it's actually logical to present the game states (<span class=\"token code-snippet code keyword\">`switch`</span> cases) in the order they have been presented: \"waiting to start,\" \"playing\" and \"dead.\"\nThat's the natural flow of the game states.\nThe initial game state is \"waiting to start,\" so the most logical position for the <span class=\"token code-snippet code keyword\">`default`</span> case is at the beginning.\n</span></code></pre>\n<h4>Code comment example</h4>\n<p>Same here:\nthis is better,\nbut the first line is too long.</p>\n<pre aria-label=\"Code block\" class=\"language-clojure bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">; No need to define separate hotstrings for uppercase letters because hotstrings are \"case-conforming\" by default.</span>\n<span class=\"token comment\">; Applies to the `;aring` → `å` hotstring as well.</span>\n</span></code></pre>\n<h3 id=\"hard-wrapping-at-eg-80-characters\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#hard-wrapping-at-eg-80-characters\">Hard wrapping at e.g. 80 characters</a></h3>\n<p>Hard wrapping used to be my favorite wrapping style.\nMy rationale back then:</p>\n<blockquote>\n<p>If you are hard wrapping your code at <em>n</em> characters,\nit's logical to hard wrap code comments at the same width.\nAnd why not hard wrap Markdown prose as well?</p>\n</blockquote>\n<p>(If you are not hard wrapping your code,\nmaybe you should.\nConfigure <a href=\"https://prettier.io/\" class=\"link link-external\">Prettier</a> or a similar tool\nand you don't have to think about it manually.)</p>\n<h4>Readability</h4>\n<p>Hard wrapped text often looks neat on the surface\nas the text has a uniform width.\nThere are no horizontal scroll bars\nor automatic hard wrapping done by the IDE,\nunless there are very long words (like links)\nor the window is very narrow.</p>\n<p>On the other hand,\nthe text is often annoying to read\nbecause hard wrapping doesn't factor in sentence structures and such.\nIt's difficult to see where sentences start and end\nand what points and ideas the whole paragraph contains.</p>\n<p>Sometimes hard wrapping can break syntax highlighting,\ne.g. when a Markdown link spans on two lines like this:</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">Here's some intro text, followed by a <span class=\"token url\">[<span class=\"token content\">link with quite a long text so that it\nspans on two lines</span>](<span class=\"token url\">https://duck.com/</span>)</span>.\n</span></code></pre>\n<p>Lastly:\norphan words are annoying,\ni.e. single words that end up on their own lines.\nCan be seen in the code comment example below.\nAlso see\n<a href=\"https://en.wikipedia.org/wiki/Widows_and_orphans\" class=\"link link-external\"><em>Widows and orphans</em> on Wikipedia</a>.</p>\n<h4>Maintainability</h4>\n<p>Very cumbersome to maintain manually.\nChange a single word in the middle of a paragraph,\nand you might have to adjust the wrapping of several lines.</p>\n<p>On the other hand,\ncan be done easily with certain tools.\nFor example:</p>\n<ul>\n<li><a href=\"https://prettier.io/\" class=\"link link-external\">Prettier</a>,\nworks at least with Markdown prose\n(not sure about code comments).</li>\n<li><a href=\"https://marketplace.visualstudio.com/items?itemName=stkb.rewrap\" class=\"link link-external\">Rewrap extension for VS Code.</a></li>\n<li><a href=\"https://marketplace.visualstudio.com/items?itemName=stkb.Rewrap-18980\" class=\"link link-external\">Rewrap extension for Visual Studio.</a></li>\n</ul>\n<p>The downside of using tools is that\nyou also have to get your teammates to\ninstall, configure and use them.</p>\n<h4>Diffability</h4>\n<p>Often abysmal.\nChange a single word in the middle of a paragraph,\nand you might have to adjust the wrapping of several lines.\nThis makes diffs very noisy.</p>\n<h4>Markdown example</h4>\n<p>Looks nice on the surface,\nbut is not very nice to read.\nThe two words inside the parentheses\nunfortunaly end up on different lines.</p>\n<p>Looking at the left side of the code block\n(the first words of the lines)\ndoesn't make much sense:\n&quot;After... cases... dead... waiting to start... the beginning.&quot;</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">After some thought, it's actually logical to present the game states (<span class=\"token code-snippet code keyword\">`switch`</span>\ncases) in the order they have been presented: \"waiting to start,\" \"playing\" and\n\"dead.\" That's the natural flow of the game states. The initial game state is\n\"waiting to start,\" so the most logical position for the <span class=\"token code-snippet code keyword\">`default`</span> case is at\nthe beginning.\n</span></code></pre>\n<h4>Code comment example</h4>\n<p>The last line contains an orphan word.\nIt looks lonely\nand makes the whole comment look unbalanced.</p>\n<p>Looking at the second line only,\nthe beginning of it doesn't make sense:\n&quot;are 'case-conforming' by default.&quot;\nWhat is it talking about?\nYou need to check the previous line\nto see that it's talking about hotstrings.</p>\n<pre aria-label=\"Code block\" class=\"language-clojure bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">; No need to define separate hotstrings for uppercase letters because hotstrings</span>\n<span class=\"token comment\">; are \"case-conforming\" by default. Applies to the `;aring` → `å` hotstring as</span>\n<span class=\"token comment\">; well.</span>\n</span></code></pre>\n<h3 id=\"semantic-line-breaks\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#semantic-line-breaks\">Semantic line breaks</a></h3>\n<p>The <a href=\"https://sembr.org/\" class=\"link link-external\">Semantic Line Breaks specification</a>\nis introed like this:</p>\n<blockquote>\n<p>When writing text with a compatible markup language,\nadd a line break after each substantial unit of thought.</p>\n</blockquote>\n<p>Semantic line breaks are my favorite!\nI have been using them at least since Nov 2020,\ni.e. for at least half a year at the time of this writing.</p>\n<h4>Readability</h4>\n<p>Lines tend to be short,\nwhich reduces the amount of horizontal eye movement,\nmaking the text easier to read.</p>\n<p>It's also easy to jump around the text vertically\nlooking for capital letters,\ni.e. where sentences begin and end.</p>\n<h4>Maintainability</h4>\n<p>A single line contains a &quot;substantial unit of thought&quot;\nlike a clause or an idea.\nThis makes editing text more powerful\nbecause it's easy to rearrange clauses and ideas\nby rearranging lines.\nCompare with e.g. hard wrapping:\nyou can't just rearrange lines,\nbecause the text wouldn't make sense anymore.</p>\n<p>Another benefit,\ncopied from the specification's FAQ section:</p>\n<blockquote>\n<p>Semantic line breaks make it easier to\nidentify grammatical mistakes\nand find opportunities to simplify and clarify without altering original intent.</p>\n</blockquote>\n<p>And yet another,\ncopied from a comment by Rich Morin\nin Brandon Rhodes's blog post\n<a href=\"https://rhodesmill.org/brandon/2012/one-sentence-per-line/\" class=\"link link-external\"><em>Semantic Linefeeds</em></a>:</p>\n<blockquote>\n<p>Another benefit of this technique is that\nit gives the author (or editor)\na chance to examine the text in two very different formats:\nmonospace with line breaks\nand proportional without.\nThis helps greatly in finding errors.</p>\n</blockquote>\n<p>One drawback is that\nit might be difficult to decide/learn\nwhat is a semantic line break,\nor what is a &quot;substantial unit of thought.&quot;\nThus, different people might wrap the same text differently.</p>\n<p>If you are working in a team,\nit might lead to slight inconsistencies.</p>\n<p>If you are working solo,\nit's not a problem.\nYou'll quickly come up with your own style.</p>\n<h4>Diffability</h4>\n<p>Excellent.\nSmall changes tend to produce clean diffs.</p>\n<p>For a visual example,\nsee Brandon Rhodes's blog post\n<a href=\"https://rhodesmill.org/brandon/2012/one-sentence-per-line/\" class=\"link link-external\"><em>Semantic Linefeeds</em></a>.</p>\n<h4>Markdown example</h4>\n<p>The text looks a bit unbalanced,\nbut each line contains a single thing.\nI'm sure someone else would wrap this code differently,\neven if we both used semantic line breaks. :-)</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">After some thought,\nit's actually logical\nto present the game states (<span class=\"token code-snippet code keyword\">`switch`</span> cases)\nin the order they have been presented:\n\"waiting to start,\"\n\"playing\" and\n\"dead.\"\nThat's the natural flow of the game states.\nThe initial game state is \"waiting to start,\"\nso the most logical position for the <span class=\"token code-snippet code keyword\">`default`</span> case\nis at the beginning.\n</span></code></pre>\n<h4>Code comment example</h4>\n<p>Yay, the orphan word is gone.</p>\n<p>The second line makes sense on its own:\n&quot;(because) hotstrings are 'case-conforming' by default.&quot;</p>\n<pre aria-label=\"Code block\" class=\"language-clojure bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">; No need to define separate hotstrings for uppercase letters</span>\n<span class=\"token comment\">; because hotstrings are \"case-conforming\" by default.</span>\n<span class=\"token comment\">; Applies to the `;aring` → `å` hotstring as well.</span>\n</span></code></pre>\n<h3 id=\"1-chaos\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#1-chaos\">+1: Chaos</a></h3>\n<p>If you don't deliberately choose a specific style\nfor wrapping code comments and prose,\nyou'll eventually have lots of inconsistencies.\nInconsistencies lead to chaos,\nand chaos means poor readability and maintainability.</p>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<p>Hat tip to the\n<a href=\"https://sembr.org/\" class=\"link link-external\">Semantic Line Breaks specification</a>\nfor teaching me a better alternative to hard wrapping.\nMy earlier blog post\n<a href=\"https://mtsknn.fi/blog/rfc-2119-in-a-nutshell/\" class=\"link\"><em>RFC 2119 in a nutshell</em></a>\nmight be helpful for interpreting the specification.</p>\n<p>Brandon Rhodes's blog post\n<a href=\"https://rhodesmill.org/brandon/2012/one-sentence-per-line/\" class=\"link link-external\"><em>Semantic Linefeeds</em></a>\nfrom 2012\ncovers the same general idea as semantic line breaks.\nIt has an example of the diffing benefits\nand musings on the history of semantic line breaks.</p>\n","summary":"A paragraph per line,\na sentence per line,\nor hard wrapping at e.g. 80 characters?\nThese days I prefer a fourth option:\nsemantic line breaks.","date_published":"2021-05-12T12:00:00+03:00","tags":["Clean code","Documentation","Markdown"]},{"id":"https://mtsknn.fi/blog/react-two-way-data-binding/","url":"https://mtsknn.fi/blog/react-two-way-data-binding/","title":"Two-way data binding in React","content_html":"<p>Normally you would use one-way data binding in React apps:\nparent components own data and pass it to children.\nYou can mimic two-way data binding with a custom hook.</p>\n<h2 id=\"one-way-data-binding\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#one-way-data-binding\">One-way data binding</a></h2>\n<p>React uses one-way data binding\nfor controlled components:\na parent passes to its child\nthe current state value\nand a function to update the value.\nFor example:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">App</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>value<span class=\"token punctuation\">,</span> setValue<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">'John'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">update</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">e</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setValue</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>target<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">reset</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setValue</span><span class=\"token punctuation\">(</span><span class=\"token string\">''</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token attr-name\">value</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>value<span class=\"token punctuation\">}</span></span> <span class=\"token attr-name\">onChange</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>update<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Hello </span><span class=\"token punctuation\">{</span>value<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>reset<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Reset</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span></span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>This way the input element (the child) never mutates the state.\nInstead it calls the <code>update</code> function (via the <code>onChange</code> event),\nso that the <em>parent</em> can update the state\nand pass the new value to the input element.</p>\n<p>So far this is very basic stuff.</p>\n<p>AngularJS 1 used to have two-way data binding:\na parent passes only the state to its child,\nand the child can directly mutate the state!</p>\n<ul>\n<li>If the parent updates the state,\nthe child will receive the new value.</li>\n<li>If the child updates the state,\nthe parent will receive the new value.</li>\n</ul>\n<p>When the parent receives a new value,\nit can't know where the new value is coming from,\nand it can't prevent the child from updating the value.</p>\n<p>Two-way data binding is nowadays considered a bad practice\nbecause it can easily lead to\nunpredictability (can't know what's changing state)\nand poor performance.\nPossibly for other reasons too.</p>\n<h2 id=\"two-way-data-binding\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#two-way-data-binding\">Two-way data binding</a></h2>\n<p>On the upside,\ntwo-way data binding makes code cleaner\nas you don't have to manually deal with event listeners and stuff.\nJust pass a state to a child and you're done.</p>\n<p><a href=\"https://sandroroth.com/blog/react-two-way-data-binding\" class=\"link link-external\">Sandro Roth's blog post <em>Two-way Data Binding in React</em></a>\nshows how you can mimic two-way data binding in React by writing a custom hook.\nLike this:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">useModel</span><span class=\"token punctuation\">(</span>initialValue <span class=\"token operator\">=</span> <span class=\"token string\">''</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>value<span class=\"token punctuation\">,</span> setValue<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span>initialValue<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">update</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">e</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setValue</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">.</span>currentTarget<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">model</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> value<span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">onChange</span><span class=\"token operator\">:</span> update <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">setModel</span><span class=\"token operator\">:</span> setValue<span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">App</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> model<span class=\"token punctuation\">,</span> setModel <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">useModel</span><span class=\"token punctuation\">(</span><span class=\"token string\">'John'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">reset</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">setModel</span><span class=\"token punctuation\">(</span><span class=\"token string\">''</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token spread\"><span class=\"token punctuation\">{</span><span class=\"token operator\">...</span>model<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Hello </span><span class=\"token punctuation\">{</span>model<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">onClick</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>reset<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Reset</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span></span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Notice how we are passing (via spreading) only a &quot;model&quot; to the input element.\nThe model contains the current value\nand a function to update the value from the child.</p>\n<p>This is ultimately still one-way data binding –\nthe parent is still the single source of truth for the state –\nbut it looks cleaner\nand allows reusage of the logic contained in the <code>useModel</code> hook.</p>\n<p><a href=\"https://sandroroth.com/blog/react-two-way-data-binding\" class=\"link link-external\">Check out Sandro's article for more details!</a></p>\n<p>(Also thanks to Sandro for our email exchange over this topic. 😊)</p>\n","summary":"Normally you would use one-way data binding in React apps:\nparent components own data and pass it to children.\nYou can mimic two-way data binding with a custom hook.","date_published":"2021-05-08T12:00:00+03:00","tags":["JavaScript","React"]},{"id":"https://mtsknn.fi/blog/site-specific-components/","url":"https://mtsknn.fi/blog/site-specific-components/","title":"Site-specific components in a (React) multi-site","content_html":"<p>A multi-site React project has likely two kinds of components:\ncommon components and site-specific components.\nThere are a few ways to handle site-specific logic in common components.</p>\n<ul>\n<li>Common components are used by two or more sites\nand reside e.g. under the <code>src/common/</code> folder.</li>\n<li>Site-specific components are used only by one site\nand reside e.g. under the folders <code>src/site-a/</code>, <code>src/site-b/</code> and so on.</li>\n</ul>\n<p>An example of a common component is <code>Link</code>:\na hyperlink with certain styles and logic,\nused on all sites for most links.</p>\n<p>Let's say that Site A,\nand only that site,\nrequires some extra logic in the <code>Link</code> component.\nWhat are my options?\nLet's think:</p>\n<ol>\n<li>Add the extra logic to the common <code>Link</code> component.\nThis would be messy,\nbecause the common component would then have site-specific logic in it.</li>\n<li>Create an entirely separate, site-specific <code>Link</code> component for Site A.\nI don't like this option\nbecause the common and site-specific <code>Link</code> components would have lots of duplicate code.\nLinks are very common,\nso any future changes would have to be made to two components.</li>\n<li>Create a &quot;child component&quot; for Site A,\nso that the site-specific component\nultimately renders the common <code>Link</code> component.\nThis would be nice,\nbut the problem would be that\ndevelopers would need to remember to import the site-specific component when working on Site A's code,\nand to import the common component when working on the other sites' codes.</li>\n</ol>\n<p>I eventually found a way to avoid the problem in the third option!\nBehold:\n<a href=\"/blog/eslint-import-restrictions/\" class=\"link\">Using ESLint to restrict where files can be imported from</a>.</p>\n","summary":"A multi-site React project has likely two kinds of components:\ncommon components and site-specific components.\nThere are a few ways to handle site-specific logic in common components.","date_published":"2021-05-01T12:00:00+03:00","date_modified":"2021-08-17T12:00:00+03:00","tags":["Clean code","JavaScript","React"]},{"id":"https://mtsknn.fi/blog/eslint-import-restrictions/","url":"https://mtsknn.fi/blog/eslint-import-restrictions/","title":"Using ESLint to restrict where files can be imported from","content_html":"<p>There's an ESLint rule to prevent importing certain files in certain folders.\nI needed this in a multi-site project\nto prevent importing site-specific components in other sites' folders.</p>\n<p>I'm talking about the\n<a href=\"https://github.com/benmosher/eslint-plugin-import/blob/HEAD/docs/rules/no-restricted-paths.md\" class=\"link link-external\"><code>no-restricted-paths</code> ESLint rule</a>,\nprovided by the <a href=\"https://github.com/benmosher/eslint-plugin-import\" class=\"link link-external\"><code>eslint-plugin-import</code> plugin</a>:</p>\n<blockquote>\n<p>Some projects contain files which are not always meant to be executed in the same environment.\nFor example consider a web application that contains specific code for the server and some specific code for the browser/client.\nIn this case you don't want to import server-only files in your client code.</p>\n</blockquote>\n<p>In my case I needed the rule to make using site-specific components more convenient.\nContext: <a href=\"/blog/site-specific-components/\" class=\"link\">Site-specific components in a multi-site (React) project</a>.</p>\n<p>Here's what I added to the project's ESLint config\nto solve the problem discussed in the linked blog post:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">module<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token string-property property\">'import/no-restricted-paths'</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">'error'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">{</span>\n      <span class=\"token literal-property property\">zones</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">target</span><span class=\"token operator\">:</span> <span class=\"token string\">'src/site-a/'</span><span class=\"token punctuation\">,</span>\n          <span class=\"token literal-property property\">from</span><span class=\"token operator\">:</span> <span class=\"token string\">'src/common/Link.js'</span><span class=\"token punctuation\">,</span>\n          <span class=\"token literal-property property\">message</span><span class=\"token operator\">:</span> <span class=\"token string\">'Import the site-specific Link component instead.'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>The config makes ESLint throw an error\nif <code>src/common/Link.js</code> is imported in any file under <code>src/site-a/</code>.\nPlus ESLint will show the custom message\nso no one has to guess why importing the common <code>Link</code> component fails.\nExcellent!</p>\n<p>To prevent importing certain npm packages,\nyou could use\n<a href=\"https://eslint.org/docs/rules/no-restricted-imports\" class=\"link link-external\">ESLint's native rule <code>no-restricted-imports</code></a>.\nA useful example case is\nif you want developers to import your own <code>Link</code> component\ninstead of React Router's <code>Link</code> component.</p>\n","summary":"There's an ESLint rule to prevent importing certain files in certain folders.\nI needed this in a multi-site project\nto prevent importing site-specific components in other sites' folders.","date_published":"2021-05-01T12:00:00+03:00","tags":["ESLint","JavaScript"]},{"id":"https://mtsknn.fi/blog/logitech-brio/","url":"https://mtsknn.fi/blog/logitech-brio/","title":"My new webcam: Logitech BRIO","content_html":"<p>Maybe a bit of an overkill,\nbut much more convenient than using my iPad as a webcam\nor my laptop's integrated webcam.</p>\n<h2 id=\"my-previous-webcams\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#my-previous-webcams\">My previous webcams</a></h2>\n<p>I have previously used my iPad mini as a webcam\nvia the <a href=\"https://www.elgato.com/en/epoccam\" class=\"link link-external\">EpocCam app</a>.\nIt has worked well,\nbut it's always a hassle to set up a stand for the iPad.\nThe iPad is also blocking a big portion of my monitor.</p>\n<p>My laptop has an integrated webcam,\nbut it's too far to the side\nwhen using an external monitor,\nso I haven't used it.\nI suspect it wouldn't even have good quality.</p>\n<h2 id=\"logitech-brio\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#logitech-brio\">Logitech BRIO</a></h2>\n<p>Some months ago I bought a <a href=\"/blog/my-new-microphone-rode-procaster/\" class=\"link\">new microphone</a>.</p>\n<p>This week I bought a new webcam:\n<a href=\"https://www.logitech.com/en-us/products/webcams/brio-4k-hdr-webcam.html\" class=\"link link-external\">Logitech BRIO</a>.</p>\n<p>I already used the BRIO in one Teams meeting,\nand it worked well.</p>\n<p>I like that the BRIO just stays on top of my monitor.\nThere's no setting it up and opening an app\nevery time I want to use it,\nunlike when using iPad via EpocCam.</p>\n","summary":"Maybe a bit of an overkill,\nbut much more convenient than using my iPad as a webcam\nor my laptop's integrated webcam.","date_published":"2021-04-24T12:00:00+03:00","tags":["Battlestation"]},{"id":"https://mtsknn.fi/blog/frequency-illusion/","url":"https://mtsknn.fi/blog/frequency-illusion/","title":"Frequency illusion","content_html":"<p>That cool thing you just heard about –\nis it suddenly gaining popularity,\nor are you just noticing the topic more?</p>\n<p>After learning about a new topic,\nit sometimes seems to quickly surge in popularity.\nLike &quot;wow, this topic is nowadays being covered <em>everywhere</em>&quot;\nor &quot;awareness of this topic is clearly increasing, nice.&quot;</p>\n<p>That's possible.\nOr, it's also possible that you are just noticing the topic more.</p>\n<p>Here's what Wikipedia tells us about\n<a href=\"https://en.wikipedia.org/wiki/Frequency_illusion\" class=\"link link-external\">frequency illusion</a>:</p>\n<blockquote>\n<p><strong>Frequency illusion</strong>,\nalso known as the <strong>Baader–Meinhof phenomenon</strong>,\nis a cognitive bias in which,\nafter noticing something for the first time,\nthere is a tendency to notice it more often,\nleading someone to believe that it has a high frequency (a form of selection bias).\nIt occurs when increased awareness of something\ncreates the illusion that it is appearing more often.\nPut plainly,\nthe frequency illusion is when\n&quot;a concept or thing you just found out about\nsuddenly seems to crop up everywhere.&quot;</p>\n</blockquote>\n<p>I had been looking for a term for this phenomenon.\nNow let's see if I start to notice lots of coverage of the frequency illusion.</p>\n","summary":"That cool thing you just heard about –\nis it suddenly gaining popularity,\nor are you just noticing the topic more?","date_published":"2021-04-24T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/css-vertical-margin-auto/","url":"https://mtsknn.fi/blog/css-vertical-margin-auto/","title":"\\*Vertical* centering in CSS with `margin: auto`","content_html":"<p><code>margin: auto</code> is not only for horizontal centering.</p>\n<p><code>margin: auto</code> normally centers a block element only horizontally.\nTo achieve vertical centering,\nit's common to reach for e.g. flexbox.</p>\n<p>Turns out that <code>margin: auto</code> can in some cases center vertically as well.\nThe element must also have:</p>\n<ul>\n<li><code>position: absolute</code> or <code>position: fixed</code></li>\n<li><code>top: 0</code> and <code>bottom: 0</code></li>\n<li>an explicit height (e.g. <code>height: 200px</code>).</li>\n</ul>\n<p>This can be very useful for modals.</p>\n<p>Note that for only vertical centering,\nonly <code>margin-top</code> and <code>margin-bottom</code> need to be set to <code>auto</code>;\n<code>margin-right</code> and <code>margin-left</code> can be anything.\nThis is similar to horizontal centering:\nonly <code>margin-right</code> and <code>margin-left</code> need to be set to <code>auto</code>;\n<code>margin-top</code> and <code>margin-bottom</code> can be anything.</p>\n<p><a href=\"https://flems.io/#0=N4IgzgpgNhDGAuEAmIBcIB0ALeBbKIANCAGYCWMYaA2qAHYCGuEamO+RIsA9nYn6wA8YeAE8YAPgA6dAASykZAG6zgM+fIBGDWAGsA5gCduAVzpJUCiBAAONsnV0BudRqwQy+nJYCMABj8bAA8XOQ0AdzIkeCxfAODQjVkbbjAyeDJeSwZNMG4oE0RQ13kAegAqWSUIQwzYBihZWAg+God9WXLSktl4bhtLP0SNTW54PtxB4flcBkN9BwBaPoHZBkLuadlZ+aXR8e5JtY3isLLKrG5DMgAvXngGppbEa7oOrp7rr3gpnpgSH6yIY9HYLOiLL7eY59Lagpb-QHrGGuAC+MkEpRE4gg0jogkUSgkGIJEk4kBgCEydCo6AATH5UD4fCAUQBdFFAA\" class=\"link link-external\">See an example on flems.io.</a></p>\n<p>I learned this from Josh W Comeau's\n<a href=\"https://css-for-js.dev/\" class=\"link link-external\"><em>CSS for JavaScript developers</em> course</a>\n(Module 2).</p>\n","summary":"`margin: auto` is not only for horizontal centering.","date_published":"2021-04-24T12:00:00+03:00","tags":["CSS","CSS for JS Devs course"]},{"id":"https://mtsknn.fi/blog/breaking-css-position-fixed/","url":"https://mtsknn.fi/blog/breaking-css-position-fixed/","title":"`position: fixed` breaks if any of its ancestors uses `transform`","content_html":"<p>Or <code>perspective</code> or <code>filter</code>.\nThis can easily cause confusing bugs.</p>\n<p><code>position: fixed</code> works mostly the same way as <code>position: absolute</code> does.</p>\n<ul>\n<li><code>position: absolute</code> is positioned relative to its closest positioned ancestor.</li>\n<li><code>position: fixed</code> is positioned relative to the viewport.</li>\n</ul>\n<p>However,\nin the case of <code>position: fixed</code>,\nif any of the element's ancestors (i.e. parent or any of the grandparents)\nhas a <code>transform</code> property set to something else than <code>none</code>,\nthe element's positioning &quot;breaks.&quot;\nThe fixed element is then positioned relative to that ancestor,\ni.e. <code>position: fixed</code> behaves like <code>position: absolute</code>.</p>\n<p>This can easily cause confusing bugs.</p>\n<p>I learned this from Josh W Comeau's\n<a href=\"https://css-for-js.dev/\" class=\"link link-external\"><em>CSS for JavaScript developers</em> course</a>\n(Module 2).</p>\n<p><a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed\" class=\"link link-external\">MDN's documentation on <code>position: fixed</code></a>\nmentions that\nthe <code>perspective</code> and <code>filter</code> properties also break <code>position: fixed</code>,\nbut\n&quot;there are browser inconsistencies with <code>perspective</code> and <code>filter</code>\ncontributing to containing block formation.&quot;</p>\n","summary":"Or `perspective` or `filter`.\nThis can easily cause confusing bugs.","date_published":"2021-04-24T12:00:00+03:00","tags":["CSS","CSS for JS Devs course"]},{"id":"https://mtsknn.fi/blog/js-fn-length/","url":"https://mtsknn.fi/blog/js-fn-length/","title":"JavaScript functions have a `length` property","content_html":"<p>A bit exotic, but can be useful for currying.</p>\n<h2 id=\"functionlength\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#functionlength\"><code>Function.length</code></a></h2>\n<p>The <code>length</code> property of a function\nreturns the number of required arguments:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length                    <span class=\"token comment\">//=> 0</span>\n<span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length                   <span class=\"token comment\">//=> 1</span>\n<span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length                <span class=\"token comment\">//=> 2</span>\n<span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b<span class=\"token punctuation\">,</span> c</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length             <span class=\"token comment\">//=> 3</span>\n<span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span>a<span class=\"token punctuation\">,</span> b<span class=\"token punctuation\">,</span> c <span class=\"token operator\">=</span> <span class=\"token string\">'default'</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length <span class=\"token comment\">//=> 2</span>\n<span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length          <span class=\"token comment\">//=> 1</span>\n<span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token operator\">...</span>args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>length             <span class=\"token comment\">//=> 0</span>\n</span></code></pre>\n<p>I learned this from\na <a href=\"https://twitter.com/mikaelainalem/status/1348160028772102146\" class=\"link link-external\">tweet by Mikael Ainalem</a>.</p>\n<p>See also\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length\" class=\"link link-external\"><code>Function.length</code> on MDN</a>.</p>\n<h2 id=\"trust-issues\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#trust-issues\">Trust issues</a></h2>\n<p>Robin Pokorny says that\n<a href=\"https://robinpokorny.com/blog/function-length-property-is-not-to-be-trusted/\" class=\"link link-external\">the <code>length</code> property is not to be trusted</a>\nbecause it can be overridden:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">fn</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\nfn<span class=\"token punctuation\">.</span>length <span class=\"token comment\">//=> 2</span>\n\nObject<span class=\"token punctuation\">.</span><span class=\"token function\">defineProperty</span><span class=\"token punctuation\">(</span>fn<span class=\"token punctuation\">,</span> <span class=\"token string\">'length'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">value</span><span class=\"token operator\">:</span> <span class=\"token number\">3</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\nfn<span class=\"token punctuation\">.</span>length <span class=\"token comment\">//=> 3</span>\n</span></code></pre>\n<p>But don't do that. 😄</p>\n<h2 id=\"example-currying\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-currying\">Example: currying</a></h2>\n<p>One practical use of <code>Function.length</code>\nis to use it to create a currying function:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">curry</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">fn<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  fn<span class=\"token punctuation\">.</span>length <span class=\"token operator\">></span> args<span class=\"token punctuation\">.</span>length\n    <span class=\"token operator\">?</span> <span class=\"token function\">curry</span><span class=\"token punctuation\">.</span><span class=\"token function\">bind</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> fn<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>args<span class=\"token punctuation\">)</span>\n    <span class=\"token operator\">:</span> <span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token operator\">...</span>args<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">mul</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">a<span class=\"token punctuation\">,</span> b<span class=\"token punctuation\">,</span> c</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> a <span class=\"token operator\">*</span> b <span class=\"token operator\">*</span> c\n\n<span class=\"token function\">curry</span><span class=\"token punctuation\">(</span>mul<span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 6</span>\n<span class=\"token function\">curry</span><span class=\"token punctuation\">(</span>mul<span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 6</span>\n<span class=\"token function\">curry</span><span class=\"token punctuation\">(</span>mul<span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 6</span>\n<span class=\"token function\">curry</span><span class=\"token punctuation\">(</span>mul<span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 6</span>\n<span class=\"token function\">curry</span><span class=\"token punctuation\">(</span>mul<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 6</span>\n<span class=\"token function\">curry</span><span class=\"token punctuation\">(</span>mul<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 6</span>\n<span class=\"token function\">curry</span><span class=\"token punctuation\">(</span>mul<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 6</span>\n<span class=\"token function\">curry</span><span class=\"token punctuation\">(</span>mul<span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 6</span>\n</span></code></pre>\n<p>Source of the <code>curry</code> function:\n<a href=\"https://twitter.com/babakness/status/1348683843163734016\" class=\"link link-external\">Babak's reply to Mikael's tweet</a>.</p>\n","summary":"A bit exotic, but can be useful for currying.","date_published":"2021-04-17T12:00:00+03:00","date_modified":"2022-08-14T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/css-height-and-width-logics/","url":"https://mtsknn.fi/blog/css-height-and-width-logics/","title":"Different logics behind heights and widths in CSS","content_html":"<p><code>height</code> and <code>width</code> work somewhat differently in CSS.\n<code>height</code> looks &quot;down&quot; the tree\nwhereas <code>width</code> looks &quot;up&quot; the tree.</p>\n<p>Full quote:</p>\n<blockquote>\n<p><code>height</code> tends to look &quot;down&quot; the tree,\nto determine its size based on the natural size of its contents,\nwhile <code>width</code> tends to look &quot;up&quot; the tree,\nbasing its size on the space made available by the parent.</p>\n</blockquote>\n<p>The quote is from Josh W Comeau's\n<a href=\"https://css-for-js.dev/\" class=\"link link-external\"><em>CSS for JavaScript developers</em> course</a>\n(Module 1).</p>\n<p>I don't know if the quote makes sense on its own (without more context).\nBut I thought it describes the difference between <code>height</code> and <code>width</code>\nvery clearly yet succinctly,\nso I wanted to share it here.</p>\n","summary":"`height` and `width` work somewhat differently in CSS.\n`height` looks \"down\" the tree\nwhereas `width` looks \"up\" the tree.","date_published":"2021-04-17T12:00:00+03:00","tags":["CSS","CSS for JS Devs course"]},{"id":"https://mtsknn.fi/blog/css-block-vs-inline/","url":"https://mtsknn.fi/blog/css-block-vs-inline/","title":"Block vs inline directions in CSS","content_html":"<p>Block elements stack on top of each other.\nInline elements are placed side by side.</p>\n<h2 id=\"web-pages-vs-paper-documents\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#web-pages-vs-paper-documents\">Web pages vs paper documents</a></h2>\n<p>Web pages are like paper documents.</p>\n<ul>\n<li>Individual words are <em>inline</em> and placed side by side,\nfrom left to right (or right to left in some languages).</li>\n<li>Some words are combined into <em>blocks</em>\n(e.g. headings and paragraphs)\nthat stack on top of each other,\nfrom top to bottom (or bottom to top in some obscure languages).</li>\n</ul>\n<p>So, that's the two directions of CSS: block and inline.\nAnd that's how <code>display: block</code> and <code>display: inline</code> work too:</p>\n<ul>\n<li>Block elements (<code>display: block</code>) take the full available width by default\nso that they stack on top of each other.\nThink of e.g. headings, paragraphs and <code>&lt;div&gt;</code> elements.</li>\n<li>Inline elements (<code>display: inline</code>) are placed side by side.\nThink of e.g. individual words and <code>&lt;span&gt;</code> elements.</li>\n</ul>\n<p>This is very basic,\nbut explaining block and inline directions\nby comparing a web page to a regular paper document\nwas somehow eye-opening to me.\nI like this <em>mental model</em>\nthat I learned from Josh W Comeau's\n<a href=\"https://css-for-js.dev/\" class=\"link link-external\"><em>CSS for JavaScript developers</em> course</a>\n(Module 1).</p>\n<h2 id=\"logical-properties\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#logical-properties\">Logical properties</a></h2>\n<p>Understanding the two directions is also essential for understanding\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties\" class=\"link link-external\">logical properties in CSS</a>.</p>\n<p>For example,\nwhen working with right-to-left (RTL) languages,\nyou might want to use\n<code>margin-inline-start</code> and <code>margin-inline-end</code>\ninstead of\n<code>margin-left</code> and <code>margin-right</code>.\nThe latter properties would be in reverse or &quot;wrong&quot; order\nwhen working with RTL languages.</p>\n","summary":"Block elements stack on top of each other.\nInline elements are placed side by side.","date_published":"2021-04-10T12:00:00+03:00","tags":["CSS","CSS for JS Devs course"]},{"id":"https://mtsknn.fi/blog/updating-react-context/","url":"https://mtsknn.fi/blog/updating-react-context/","title":"Updating React context outside of a component","content_html":"<p>React context is normally updated inside the React app.\nHere's a hacky way how I circumvented that restriction in a test file.</p>\n<h2 id=\"example-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-problem\">Example problem</a></h2>\n<p>Let's say I have a React component called <code>MyComponent</code>\nwhich uses a React context called <code>MyContext</code>.</p>\n<p>When the value of the context changes,\nthe component does something,\ne.g. makes an Adobe Analytics tracking call:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">MyComponent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> myContext <span class=\"token operator\">=</span> <span class=\"token function\">useContext</span><span class=\"token punctuation\">(</span>MyContext<span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    window<span class=\"token punctuation\">.</span>_satellite<span class=\"token punctuation\">.</span><span class=\"token function\">track</span><span class=\"token punctuation\">(</span>myContext<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>myContext<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">null</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>And then I want to write unit tests for that...\nBut how can I do that\nsince React contexts can't be easily updated outside of React components?</p>\n<h2 id=\"hacky-workaround\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#hacky-workaround\">Hacky workaround</a></h2>\n<p>I realized that in the test file,\nI can pass an empty object for the wrapper component (<code>App</code> in this case)\nas a prop (named <code>contextUpdater</code>).</p>\n<p>Inside <code>App</code>,\nI can store a reference to the context value setter function (<code>setValue</code> in this case)\nunder the given prop object.</p>\n<p>Then, in a test case,\nI can call <code>contextUpdater.setValue</code> –\noutside of the component!</p>\n<p>Like so:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">/* MyComponent.test.js */</span>\n\n<span class=\"token function\">describe</span><span class=\"token punctuation\">(</span><span class=\"token string\">'MyComponent'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">function</span> <span class=\"token function\">App</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> contextUpdater <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span> <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>value<span class=\"token punctuation\">,</span> setValue<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">useState</span><span class=\"token punctuation\">(</span><span class=\"token string\">'initial value'</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token comment\">// This hack allows us to update the context value outside of the component</span>\n    contextUpdater<span class=\"token punctuation\">.</span>setValue <span class=\"token operator\">=</span> setValue\n\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">MyContext.Provider</span></span> <span class=\"token attr-name\">value</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span><span class=\"token punctuation\">[</span>value<span class=\"token punctuation\">,</span> setValue<span class=\"token punctuation\">]</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">MyComponent</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span><span class=\"token class-name\">MyContext.Provider</span></span><span class=\"token punctuation\">></span></span>\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">beforeEach</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    global<span class=\"token punctuation\">.</span>_satellite <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">track</span><span class=\"token operator\">:</span> jest<span class=\"token punctuation\">.</span><span class=\"token function\">fn</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">it</span><span class=\"token punctuation\">(</span><span class=\"token string\">'makes a tracking call when MyContext changes'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> contextUpdater <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n\n    <span class=\"token function\">render</span><span class=\"token punctuation\">(</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">App</span></span> <span class=\"token attr-name\">contextUpdater</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>contextUpdater<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token punctuation\">)</span>\n    <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>global<span class=\"token punctuation\">.</span>_satellite<span class=\"token punctuation\">.</span>track<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toHaveBeenCalledTimes</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token function\">act</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> contextUpdater<span class=\"token punctuation\">.</span><span class=\"token function\">setValue</span><span class=\"token punctuation\">(</span><span class=\"token string\">'new value'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>global<span class=\"token punctuation\">.</span>_satellite<span class=\"token punctuation\">.</span>track<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toHaveBeenCalledTimes</span><span class=\"token punctuation\">(</span><span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token function\">act</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> contextUpdater<span class=\"token punctuation\">.</span><span class=\"token function\">setValue</span><span class=\"token punctuation\">(</span><span class=\"token string\">'third value'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token function\">expect</span><span class=\"token punctuation\">(</span>global<span class=\"token punctuation\">.</span>_satellite<span class=\"token punctuation\">.</span>track<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toHaveBeenCalledTimes</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>It works!\nIQ 200!\nProud moment of the week.</p>\n<h2 id=\"too-hacky\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#too-hacky\">Too hacky?</a></h2>\n<p>So yeah,\nactually this feels very hacky,\nprobably because this <em>is</em> hacky.</p>\n<p>Another question is whether the test is too implementation-specific.\nOn the other hand,\n<code>MyComponent</code> directly depends on <code>MyContext</code>,\nso I'm not sure.</p>\n<p>Anyway,\nthe test passes,\nso I think this is good enough for now.</p>\n","summary":"React context is normally updated inside the React app.\nHere's a hacky way how I circumvented that restriction in a test file.","date_published":"2021-04-03T12:00:00+03:00","tags":["JavaScript","React","Testing"]},{"id":"https://mtsknn.fi/blog/excel-durations/","url":"https://mtsknn.fi/blog/excel-durations/","title":"Excel stores time durations as days","content_html":"<p>For example, 6 hours is stored as 0.25 days.\nTo format a duration as <code>hh:mm:ss.000</code>,\nfirst convert the duration to days.</p>\n<h2 id=\"example-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-problem\">Example problem</a></h2>\n<p>A friend of mine consulted me with this problem:\nhe was copy-pasting a bunch of time durations to Excel\nand wanted to calculate their average duration.</p>\n<p>The values were not in Excel-friendly format,\nand he didn't want to modify them by hand,\nso Excel's <code>AVERAGE</code> function didn't work out of the box.</p>\n<p>Here's an example of a single time duration:</p>\n<pre aria-label=\"Code block\" class=\"language-text bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">1:39.811\n</span></code></pre>\n<p>That's one minute, 39 seconds and 811 milliseconds.</p>\n<h2 id=\"failed-attempts\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#failed-attempts\">Failed attempts</a></h2>\n<p>I first tried to change the formatting of the data,\nhoping that Excel would correctly identify the data as time durations.\nBut I couldn't get that to work.</p>\n<p>I then tried to parse the text and pass its parts to the <code>TIME</code> function\nwhich takes three parameters:\nhours, minutes and seconds.\nBut passing a decimal number (i.e. seconds with milliseconds) as the third parameter\ndidn't work.</p>\n<h2 id=\"solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#solution\">Solution</a></h2>\n<h3 id=\"converting-the-raw-data-to-milliseconds\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#converting-the-raw-data-to-milliseconds\">Converting the raw data to milliseconds</a></h3>\n<p>In the end,\nI found it easiest to convert the raw data to milliseconds\nusing Excel's <code>LEFT</code>, <code>MID</code> and <code>RIGHT</code> functions.</p>\n<p>Here's the formula for the B column,\nassuming that the original data resides in the A column:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// A1 contains a duration, e.g. \"1:39.811\"</span>\n\n<span class=\"token comment\">// B1 contains the duration in milliseconds.</span>\n<span class=\"token comment\">// In this case it would be \"99811\"</span>\n<span class=\"token operator\">=</span>\n  <span class=\"token comment\">// Minutes:</span>\n  <span class=\"token comment\">// the first character from the left.</span>\n  <span class=\"token comment\">// Doesn't support 10+ minute durations,</span>\n  <span class=\"token comment\">// but that's probably OK with this data.</span>\n  <span class=\"token constant\">LEFT</span><span class=\"token punctuation\">(</span><span class=\"token constant\">A1</span><span class=\"token punctuation\">;</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> <span class=\"token number\">1000</span> <span class=\"token operator\">*</span> <span class=\"token number\">60</span>\n\n  <span class=\"token comment\">// Seconds:</span>\n  <span class=\"token comment\">// starting from the third character (1-indexed),</span>\n  <span class=\"token comment\">// take two characters in the middle.</span>\n  <span class=\"token operator\">+</span> <span class=\"token constant\">MID</span><span class=\"token punctuation\">(</span><span class=\"token constant\">A1</span><span class=\"token punctuation\">;</span> <span class=\"token number\">3</span><span class=\"token punctuation\">;</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">*</span> <span class=\"token number\">1000</span>\n\n  <span class=\"token comment\">// Milliseconds:</span>\n  <span class=\"token comment\">// the three rightmost characters.</span>\n  <span class=\"token operator\">+</span> <span class=\"token constant\">RIGHT</span><span class=\"token punctuation\">(</span><span class=\"token constant\">A1</span><span class=\"token punctuation\">;</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Now that the B column contains the time durations in milliseconds,\nit's easy to calculate the average value:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token operator\">=</span><span class=\"token constant\">AVERAGE</span><span class=\"token punctuation\">(</span><span class=\"token constant\">B1</span><span class=\"token operator\">:</span><span class=\"token constant\">B999</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>But how to format the result as a time duration?</p>\n<h3 id=\"displaying-milliseconds-as-a-duration\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#displaying-milliseconds-as-a-duration\">Displaying milliseconds as a duration</a></h3>\n<p>Luckily,\nsomeone had asked\n<a href=\"https://old.reddit.com/r/excel/comments/5txvud/convert_milliseconds_to_hhmmss000/\" class=\"link link-external\">&quot;[How to] convert milliseconds to hh:mm:ss.000&quot; on Reddit</a>\nfour years ago,\nand someone called <em>sqylogin</em> had shared this nugget of wisdom\n(formatted for clarity):</p>\n<blockquote>\n<p>Excel stores data in days.</p>\n<p>Therefore, 1 millisecond = 1/24/60/60/1,000 = 1/86,400,000.</p>\n<p>To convert 586 milliseconds to an Excel &quot;day,&quot;\ndivide it by 86.4 million,\nand in the resulting cell,\nuse this custom number format:\n<code>HH:MM:SS.000</code>.</p>\n</blockquote>\n<p>Ah, so calculating the average time duration is as easy as this:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token operator\">=</span><span class=\"token constant\">AVERAGE</span><span class=\"token punctuation\">(</span><span class=\"token constant\">B1</span><span class=\"token operator\">:</span><span class=\"token constant\">B999</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">/</span> <span class=\"token number\">86400000</span>\n</span></code></pre>\n<p>Plus the formatting of the cell displaying the average value needs to be set to this:</p>\n<pre aria-label=\"Code block\" class=\"language-text bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">m:ss.000\n</span></code></pre>\n<p>Egg-cellent!\nNow my friend can keep copy-pasting values to the A column,\nand the average value will be updated automatically.\nHe can even hide the B column\nsince it's only used as an intermediate step to calculate the average value.</p>\n<h2 id=\"the-actual-learning\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-actual-learning\">The actual learning</a></h2>\n<p>Excel is often a very powerful and quick tool to use.\nBut sometimes it doesn't work like I would want it to,\nso I end up fighting it and coming up with hacky solutions like this.</p>\n<p>To be fair,\nmaybe that applies to all tools\nand even programming in general.</p>\n","summary":"For example, 6 hours is stored as 0.25&nbsp;days.\nTo format a duration as `hh:mm:ss.000`,\nfirst convert the duration to days.","date_published":"2021-04-03T12:00:00+03:00","tags":["Excel"]},{"id":"https://mtsknn.fi/blog/safari-pdf-error/","url":"https://mtsknn.fi/blog/safari-pdf-error/","title":"\"Blocked plug-in\" error in Safari when opening a PDF file","content_html":"<p>Try to add <code>object-src 'self';</code> to the Content Security Policy.\nThen your users don't have to download the file to view it.</p>\n<h2 id=\"the-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-problem\">The problem</a></h2>\n<p>I was investigating an issue where Safari failed to display a PDF file.\nIt instead just showed the text &quot;Blocked plug-in.&quot;</p>\n<p>The problem occurred only in desktop Safari.</p>\n<h2 id=\"the-fix\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-fix\">The fix</a></h2>\n<p>The user could download the file to view it,\nbut that would be cumbersome.</p>\n<p>I did some quick googling\nbut didn't find any good results.</p>\n<p>Then I noticed that\nSafari's console showed an error message related to\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP\" class=\"link link-external\">Content Security Policy (CSP)</a>.</p>\n<p><strong>In the end, the fix was to add <code>object-src 'self';</code> to the CSP.</strong></p>\n<p>Interesting that other browsers don't require that setting.</p>\n<p><a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/object-src\" class=\"link link-external\">Read about the <code>object-src</code> CSP directive on MDN.</a></p>\n","summary":"Try to add `object-src 'self';` to the Content Security Policy.\nThen your users don't have to download the file to view it.","date_published":"2021-03-20T12:00:00+03:00","tags":["Safari"]},{"id":"https://mtsknn.fi/blog/safari-broken-svg/","url":"https://mtsknn.fi/blog/safari-broken-svg/","title":"Broken CSS-animated SVG files in Safari and iOS Safari","content_html":"<p>Some parts of an SVG were missing and some parts were distorted.\nI had to render the SVG via an <code>&lt;svg&gt;</code> element\ninstead of loading it via an <code>&lt;img&gt;</code> element.</p>\n<h2 id=\"the-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-problem\">The problem</a></h2>\n<p>I was loading an SVG file via an <code>&lt;img&gt;</code> element.\nThe SVG file had CSS animations embedded in it.</p>\n<p>Safari and iOS Safari rendered the SVG in a very buggy way:\nsome parts were missing and some parts were distorted.</p>\n<h2 id=\"the-fix\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-fix\">The fix</a></h2>\n<p>I did some quick googling\nbut didn't find a clear cause.\nI did find some speak along the lines of\n&quot;Safari is bad at rendering SVGs.&quot;</p>\n<p><strong>In the end, the fix was to inline the SVG file.</strong>\nI.e. render it directly via an <code>&lt;svg&gt;</code> element\ninstead of loading it via an <code>&lt;img&gt;</code> element.</p>\n<p>The downside is that now the file can't be cached by the browser.</p>\n","summary":"Some parts of an SVG were missing and some parts were distorted.\nI had to render the SVG via an `<svg>` element\ninstead of loading it via an `<img>` element.","date_published":"2021-03-20T12:00:00+03:00","tags":["Safari","SVG"]},{"id":"https://mtsknn.fi/blog/firefox-devtools-inactive-css/","url":"https://mtsknn.fi/blog/firefox-devtools-inactive-css/","title":"Firefox DevTools can explain why some CSS styles have no effect","content_html":"<p>It's frustrating\nwhen a CSS declaration is technically valid\n(not crossed out in the browser's DevTools)\nbut has no effect for some reason.\nFirefox DevTools can often tell you why\nand also how to potentially fix those issues!\n(Update: Chrome 108+ can too.)</p>\n<p>Starting from\n<a href=\"https://hacks.mozilla.org/2019/10/firefox-70-a-bountiful-release-for-all/\" class=\"link link-external\">Firefox 70 (released in October 2019)</a>,\nits DevTools contain &quot;inactive CSS rules indicators&quot;\nthat explain why some styles have no effect:</p>\n<p><img src=\"./firefox-devtools-inactive-css-rule-indicator.png\" alt=\"Firefox DevTools' inactive CSS rule indicator. Here it's saying that &quot;width has no effect on this element since it has a display of inline. Try adding display:inline-block or display:block.&quot;\"></p>\n<p>I use Firefox as my daily browser,\nbut usually switch to Chrome when doing web development\nbecause Firefox DevTools often feel buggy.\nGood to know that\nFirefox DevTools can be very helpful in these frustrating CSS situations.</p>\n<p>I learned this trick from Josh W Comeau's\n<a href=\"https://css-for-js.dev/\" class=\"link link-external\"><em>CSS for JavaScript Developers</em> course</a>\n(Module 0).</p>\n<p>Later I also found Elijah Manor's\n<a href=\"https://elijahmanor.com/blog/firefox-devtools-inactive-css\" class=\"link link-external\">video explaining the feature</a>.</p>\n<p><strong>Update on Oct 13, 2022:</strong>\nJecelyn Yeen just tweeted that\n<a href=\"https://twitter.com/jecfish/status/1580135502635671552\" class=\"link link-external\">Chrome Canary has now a similar feature</a>.</p>\n<p><strong>Update on Dec 5, 2022:</strong>\nThe feature is now part of stable Chrome:\n<a href=\"https://developer.chrome.com/en/blog/new-in-devtools-108/\" class=\"link link-external\"><em>What's New In DevTools (Chrome 108)</em></a>.</p>\n","summary":"It's frustrating\nwhen a CSS declaration is valid\nbut has no effect for some reason.\nFirefox DevTools can tell you why\nand also how to fix the issue!\n(Update: Chrome 108+ can too.)","date_published":"2021-03-20T12:00:00+03:00","date_modified":"2022-12-05T12:00:00+03:00","tags":["CSS","CSS for JS Devs course"]},{"id":"https://mtsknn.fi/blog/4-types-of-documentation/","url":"https://mtsknn.fi/blog/4-types-of-documentation/","title":"4+ types of documentation","content_html":"<p>Splitting documentation into multiple categories\ncan make the documentation much clearer.</p>\n<h2 id=\"4-types\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#4-types\">4 types</a></h2>\n<p><a href=\"https://documentation.divio.com/\" class=\"link link-external\">Divio's documentation system</a>\nsplits documentation into four types:</p>\n<ul>\n<li><strong>Tutorials</strong> are learning-oriented;\nthey teach how to get started.</li>\n<li><strong>How-to guides</strong> are problem-oriented;\nthey guide how to achieve a certain goal.</li>\n<li><strong>Explanations</strong> are understanding-oriented;\nthey clarify a certain topic.</li>\n<li><strong>References</strong> are information-oriented;\nthey describe the system and how to use it.\n(E.g. what methods are available,\nwhat parameters they take\nand what they return.)</li>\n</ul>\n<p>The four types are quite similar to each other.</p>\n<p>But if you don't realize that there are these different types of documentation,\nthe types get mixed very easily,\nand the documentation gets messy.</p>\n<h3 id=\"alternative-descriptions\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#alternative-descriptions\">Alternative descriptions</a></h3>\n<p>From <a href=\"https://old.reddit.com/r/ExperiencedDevs/comments/nmodyl/drunk_post_things_ive_learned_as_a_sr_engineer/h01mflf/\" class=\"link link-external\">a Reddit comment by Kinrany</a>:</p>\n<ul>\n<li><strong>Tutorial</strong>: practical steps for learning.</li>\n<li><strong>How-to guide</strong>: practical steps for working.</li>\n<li><strong>Explanation</strong>: theoretical knowledge for learning.</li>\n<li><strong>Reference</strong>: theoretical knowledge for working.</li>\n</ul>\n<h2 id=\"different-names\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#different-names\">Different names</a></h2>\n<p>A good point from a\n<a href=\"https://news.ycombinator.com/item?id=26002656\" class=\"link link-external\">related discussion on Hacker News</a>:\nthe different types may have different names.</p>\n<p>For example,\nexplanations could also be called:</p>\n<ul>\n<li>Backgrounds</li>\n<li>Deep dives</li>\n<li>Discussions</li>\n<li>Overviews</li>\n<li>Rationales.</li>\n</ul>\n<h2 id=\"more-types\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#more-types\">More types</a></h2>\n<p>Another good point from the Hacker News discussion:\nthere can be other types of documentation as well.</p>\n<p>For example:</p>\n<ul>\n<li>Introduction</li>\n<li>Announcements</li>\n<li>Case studies</li>\n<li>Code samples</li>\n<li>Release notes.</li>\n</ul>\n<p>These types might fit into one of the four categories,\nor they might not.</p>\n","summary":"Splitting documentation into multiple categories\ncan make the documentation much clearer.","date_published":"2021-03-20T12:00:00+03:00","date_modified":"2022-04-29T12:00:00+03:00","tags":["Documentation"]},{"id":"https://mtsknn.fi/blog/ts-param-props/","url":"https://mtsknn.fi/blog/ts-param-props/","title":"Turning constructor params into class props in TypeScript","content_html":"<p>&quot;Parameter properties&quot; reduce boilerplate in classes,\nbut they can also make the code hard to read.</p>\n<h2 id=\"definition-of-parameter-properties\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#definition-of-parameter-properties\">Definition of parameter properties</a></h2>\n<p><a href=\"https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties\" class=\"link link-external\"><em>Parameter Properties</em> are defined in the TypeScript docs</a>\nlike so:</p>\n<blockquote>\n<p>TypeScript offers special syntax\nfor turning a constructor parameter\ninto a class property\nwith the same name and value.\nThese are called <em>parameter properties</em>\nand are created by prefixing a constructor argument\nwith one of the visibility modifiers\n<code>public</code>, <code>private</code>, <code>protected</code>, or <code>readonly</code>.</p>\n</blockquote>\n<h2 id=\"example\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example\">Example</a></h2>\n<p>The two classes below are effectively identical.</p>\n<h3 id=\"before-ordinary-class-properties\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#before-ordinary-class-properties\">Before: ordinary class properties</a></h3>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Ordinary class properties</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Foo</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">public</span> <span class=\"token keyword\">readonly</span> x<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span>\n  <span class=\"token keyword\">protected</span> y<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span>\n  <span class=\"token keyword\">private</span> z<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span>\n\n  <span class=\"token function\">constructor</span><span class=\"token punctuation\">(</span>x<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">,</span> y<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">,</span> z<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>x <span class=\"token operator\">=</span> x\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>y <span class=\"token operator\">=</span> y\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>z <span class=\"token operator\">=</span> z\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h3 id=\"after-parameter-properties\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#after-parameter-properties\">After: parameter properties</a></h3>\n<pre aria-label=\"Code block\" class=\"language-ts bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Parameter properties</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Foo</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">constructor</span><span class=\"token punctuation\">(</span>\n    <span class=\"token keyword\">public</span> <span class=\"token keyword\">readonly</span> x<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">protected</span> y<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span><span class=\"token punctuation\">,</span>\n    <span class=\"token keyword\">private</span> z<span class=\"token operator\">:</span> <span class=\"token builtin\">number</span>\n  <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h2 id=\"verdict\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#verdict\">Verdict</a></h2>\n<p>I like that parameter properties reduce boilerplate and verbosity.\nBut they are potentially confusing\nif they are mixed with ordinary class properties –\nit might be hard to see at a glance all the properties that the class has.</p>\n","summary":"\"Parameter properties\" reduce boilerplate in classes,\nbut they can also make the code hard to read.","date_published":"2021-03-13T12:00:00+03:00","tags":["JavaScript","TypeScript"]},{"id":"https://mtsknn.fi/blog/js-leap-year/","url":"https://mtsknn.fi/blog/js-leap-year/","title":"Cool way to check for leap years in JavaScript","content_html":"<p>Spoiler:\n<code>new Date(year, 1, 29).getDate() === 29</code>.</p>\n<p>I stumbled upon\n<a href=\"https://twitter.com/colindecarlo/status/1360617963297112066\" class=\"link link-external\">a tweet by Colin DeCarlo</a>\nabout a cool way to check if a given year is a leap year\n(code by JleCoRyl3):</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">isLearYear</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">year</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span>year<span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">29</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">getDate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">===</span> <span class=\"token number\">29</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Quite clever!\nThough a bit confusing at first\nbecause months are 0-indexed.\nMaybe a code comment would be in order.</p>\n<p>The tweet is what inspired me to start doing\nTypeScript exercises on <a href=\"https://exercism.org/\" class=\"link link-external\">Exercism</a>.</p>\n","summary":"Spoiler:\n`new Date(year, 1, 29).getDate() === 29`.","date_published":"2021-03-13T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/csharp-nsubstitute/","url":"https://mtsknn.fi/blog/csharp-nsubstitute/","title":"Cleaner unit test mocking in C# with NSubstitute","content_html":"<p>The NSubstitute package simplifies\nhow mock implementations are created\nand how mocks are used.</p>\n<h2 id=\"moq\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#moq\">Moq</a></h2>\n<p>I have previously used\n<a href=\"https://github.com/moq/moq4\" class=\"link link-external\">Moq, &quot;the most popular and friendly mocking library for .NET,&quot;</a>\nto do mocking in unit tests.</p>\n<p>Example from <a href=\"https://www.youtube.com/watch?v=9ZvDBSQa_so\" class=\"link link-external\">Nick Chapsas's video on the Moq package</a>\n(<code>_sut</code> = system under test):</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">private</span> <span class=\"token keyword\">readonly</span> <span class=\"token class-name\">CustomerService</span> _sut<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">private</span> <span class=\"token keyword\">readonly</span> <span class=\"token class-name\">ICustomerRepository</span> _customerRepositoryMock <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">Mock<span class=\"token punctuation\">&lt;</span>ICustomerRepository<span class=\"token punctuation\">></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">private</span> <span class=\"token keyword\">readonly</span> <span class=\"token class-name\">ILoggingService</span> _loggerMock <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">Mock<span class=\"token punctuation\">&lt;</span>ILoggingService<span class=\"token punctuation\">></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">public</span> <span class=\"token function\">CustomerServiceTests</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">{</span>\n    _sut <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">CustomerService</span><span class=\"token punctuation\">(</span>_customerRepositoryMock<span class=\"token punctuation\">.</span>Object<span class=\"token punctuation\">,</span> _loggerMock<span class=\"token punctuation\">.</span>Object<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token punctuation\">[</span><span class=\"token attribute\"><span class=\"token class-name\">Fact</span></span><span class=\"token punctuation\">]</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">async</span> <span class=\"token return-type class-name\">Task</span> <span class=\"token function\">GetByIdAsync_ShouldReturnCustomer_WhenCustomerExists</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n\n    <span class=\"token comment\">// Mock implementation</span>\n    _customerRepositoryMock<span class=\"token punctuation\">.</span><span class=\"token function\">Setup</span><span class=\"token punctuation\">(</span>x <span class=\"token operator\">=></span> x<span class=\"token punctuation\">.</span><span class=\"token function\">GetByIdAsync</span><span class=\"token punctuation\">(</span>customerId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">.</span><span class=\"token function\">ReturnsAsync</span><span class=\"token punctuation\">(</span>customerDto<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h2 id=\"nsubstitute\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#nsubstitute\">NSubstitute</a></h2>\n<p><a href=\"https://www.youtube.com/watch?v=LcQYv0cBWk0\" class=\"link link-external\">Nick Chapsas's video on the NSubstitute package</a>\nconvinced me that\n<a href=\"https://nsubstitute.github.io/\" class=\"link link-external\">NSubstitute, &quot;a friendly substitute for .NET mocking libraries,&quot;</a>\ncould be nicer.</p>\n<p>Example from the video:</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">private</span> <span class=\"token keyword\">readonly</span> <span class=\"token class-name\">CustomerService</span> _sut<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">private</span> <span class=\"token keyword\">readonly</span> <span class=\"token class-name\">ICustomerRepository</span> _customerRepository <span class=\"token operator\">=</span> Substitute<span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">For</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>ICustomerRepository<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">private</span> <span class=\"token keyword\">readonly</span> <span class=\"token class-name\">ILoggingService</span> _logger <span class=\"token operator\">=</span> Substitute<span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">For</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>ILoggingService<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">public</span> <span class=\"token function\">CustomerServiceTests</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">{</span>\n    _sut <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">CustomerService</span><span class=\"token punctuation\">(</span>_customerRepository<span class=\"token punctuation\">,</span> _logger<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token punctuation\">[</span><span class=\"token attribute\"><span class=\"token class-name\">Fact</span></span><span class=\"token punctuation\">]</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">async</span> <span class=\"token return-type class-name\">Task</span> <span class=\"token function\">GetByIdAsync_ShouldReturnCustomer_WhenCustomerExists</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n\n    <span class=\"token comment\">// Mock implementation</span>\n    _customerRepository<span class=\"token punctuation\">.</span><span class=\"token function\">GetByIdAsync</span><span class=\"token punctuation\">(</span>customerId<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">Returns</span><span class=\"token punctuation\">(</span>customerDto<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h3 id=\"differences-to-moq\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#differences-to-moq\">Differences to Moq</a></h3>\n<ul>\n<li>No need to use <code>.Object</code> in various places.</li>\n<li>Creating mock implementations is more straightforward.</li>\n</ul>\n","summary":"The NSubstitute package simplifies\nhow mock implementations are created\nand how mocks are used.","date_published":"2021-03-13T12:00:00+03:00","tags":["C#","Testing"]},{"id":"https://mtsknn.fi/blog/csharp-fluent-assertions/","url":"https://mtsknn.fi/blog/csharp-fluent-assertions/","title":"Cleaner unit test assertions in C# with Fluent Assertions","content_html":"<p>The Fluent Assertions package makes unit test assertions read like English\nand also improves the error messages.</p>\n<p>Watching <a href=\"https://www.youtube.com/watch?v=b2zxl5zNjlA\" class=\"link link-external\">Nick Chapsas's video on the Fluent Assertions package</a>\nmade me want to start using <a href=\"https://fluentassertions.com/\" class=\"link link-external\">Fluent Assertions</a> at work.</p>\n<p>Example from the video (<code>_sut</code> = system under test):</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Before</span>\nAssert<span class=\"token punctuation\">.</span><span class=\"token function\">Equal</span><span class=\"token punctuation\">(</span>expected<span class=\"token punctuation\">,</span> result<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nAssert<span class=\"token punctuation\">.</span><span class=\"token function\">StartsWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"The result is: \"</span><span class=\"token punctuation\">,</span> _sut<span class=\"token punctuation\">.</span>Text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nAssert<span class=\"token punctuation\">.</span><span class=\"token function\">EndsWith</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">.</span><span class=\"token function\">ToString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> _sut<span class=\"token punctuation\">.</span>Text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// After</span>\nresult<span class=\"token punctuation\">.</span><span class=\"token function\">Should</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">Be</span><span class=\"token punctuation\">(</span>expected<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n_sut<span class=\"token punctuation\">.</span>Text<span class=\"token punctuation\">.</span><span class=\"token function\">Should</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">StartWith</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"The result is: \"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n_sut<span class=\"token punctuation\">.</span>Text<span class=\"token punctuation\">.</span><span class=\"token function\">Should</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">EndWith</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">.</span><span class=\"token function\">ToString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n<p>So much better.\nReads like English.</p>\n<p>Another benefit is that the messages from failed assertions are more detailed.</p>\n","summary":"The Fluent Assertions package makes unit test assertions read like English\nand also improves the error messages.","date_published":"2021-03-13T12:00:00+03:00","tags":["C#","Testing"]},{"id":"https://mtsknn.fi/blog/how-to-disable-imageprocessors-upscaling-feature/","url":"https://mtsknn.fi/blog/how-to-disable-imageprocessors-upscaling-feature/","title":"How to disable ImageProcessor's upscaling feature","content_html":"<p>Disabling the upscaling feature (by default or completely)\ncan be done via ImageProcessor's <code>ValidatingRequest</code> event.\nIt's not possible via a config file.</p>\n<h2 id=\"whats-upscaling\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#whats-upscaling\">What's upscaling?</a></h2>\n<p><a href=\"https://imageprocessor.org/imageprocessor-web/imageprocessingmodule/resize/\" class=\"link link-external\">ImageProcessor's resize method</a>\nlets you modify the dimensions of an image on the fly,\nby using URL parameters.\nFor example:</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>img</span> <span class=\"token attr-name\">alt</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span><span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">src</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/my-image.jpg?width=2000&amp;height=1000<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>img</span> <span class=\"token attr-name\">alt</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span><span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">src</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/my-image.jpg?width=2000&amp;heightratio=0.5625<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n</span></code></pre>\n<p>Upscaling means that\n<strong>an image is resized to dimensions greater than its original dimensions</strong>.\nThis can cause blurry images,\nso you might want to disable the upscaling feature.</p>\n<p>You can disable upscaling on a case-by-case basis\nwith the <code>upscale</code> URL parameter:</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>img</span> <span class=\"token attr-name\">alt</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span><span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">src</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/my-image.jpg?width=2000&amp;height=1000&amp;upscale=false<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token comment\">&lt;!--                                                 ^^^^^^^^^^^^^^ --></span>\n</span></code></pre>\n<p>Doing so manually will however quickly become cumbersome.\nLet's see how to disable it programmatically.</p>\n<h2 id=\"not-possible-via-a-config-file\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#not-possible-via-a-config-file\">Not possible via a config file</a></h2>\n<p>According to\n<a href=\"https://github.com/JimBobSquarePants/ImageProcessor/issues/583\" class=\"link link-external\">GitHub issue #583</a>\nand\n<a href=\"https://github.com/JimBobSquarePants/ImageProcessor/issues/767\" class=\"link link-external\">issue #767</a>,\nit's not possible to disable the upscaling feature via a config file.</p>\n<h2 id=\"disable-via-an-event\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#disable-via-an-event\">Disable via an event</a></h2>\n<p>The disabling of the upscaling feature\nneeds to be done via ImageProcessor's <code>ValidatingRequest</code> event.\nSee the\n<a href=\"https://imageprocessor.org/imageprocessor-web/imageprocessingmodule/#events\" class=\"link link-external\"><em>Events</em> section in ImageProcessor's docs</a>.</p>\n<p>Add the code maybe to the <code>Application_Start</code> event in <code>Global.asax</code>.\nOr if using Episerver,\n<a href=\"https://world.episerver.com/documentation/developer-guides/CMS/initialization/Creating-an-initialization-module/\" class=\"link link-external\">create an initialization module</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">[</span><span class=\"token attribute\"><span class=\"token class-name\">InitializableModule</span></span><span class=\"token punctuation\">]</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">ImageProcessorEventsInitialization</span> <span class=\"token punctuation\">:</span> <span class=\"token type-list\"><span class=\"token class-name\">IInitializableModule</span></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Initialize</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">InitializationEngine</span> context<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        ImageProcessingModule<span class=\"token punctuation\">.</span>ValidatingRequest <span class=\"token operator\">+=</span> DisableUpscalingByDefault<span class=\"token punctuation\">;</span>\n        <span class=\"token comment\">// or</span>\n        ImageProcessingModule<span class=\"token punctuation\">.</span>ValidatingRequest <span class=\"token operator\">+=</span> DisableUpscaling<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Uninitialize</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">InitializationEngine</span> context<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        ImageProcessingModule<span class=\"token punctuation\">.</span>ValidatingRequest <span class=\"token operator\">-=</span> DisableUpscalingByDefault<span class=\"token punctuation\">;</span>\n        <span class=\"token comment\">// or</span>\n        ImageProcessingModule<span class=\"token punctuation\">.</span>ValidatingRequest <span class=\"token operator\">-=</span> DisableUpscaling<span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">private</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">DisableUpscalingByDefault</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">ImageProcessingModule</span> sender<span class=\"token punctuation\">,</span> <span class=\"token class-name\">ValidatingRequestEventArgs</span> args<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// ...</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token comment\">// or</span>\n    <span class=\"token keyword\">private</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">DisableUpscaling</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">ImageProcessingModule</span> sender<span class=\"token punctuation\">,</span> <span class=\"token class-name\">ValidatingRequestEventArgs</span> args<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">// ...</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h3 id=\"disable-by-default\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#disable-by-default\">Disable by default</a></h3>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">/// &lt;summary></span>\n<span class=\"token comment\">///     Disable ImageProcessor's upscaling feature by default.</span>\n<span class=\"token comment\">///     Upscaling can be enabled for individual images</span>\n<span class=\"token comment\">///     with the &lt;c>upscale=true&lt;/c> URL parameter.</span>\n<span class=\"token comment\">/// &lt;/summary></span>\n<span class=\"token comment\">/// &lt;remarks></span>\n<span class=\"token comment\">///     Not possible via configuration:</span>\n<span class=\"token comment\">///     &lt;list type=\"bullet\"></span>\n<span class=\"token comment\">///         &lt;item>https://imageprocessor.org/imageprocessor-web/imageprocessingmodule/#events&lt;/item></span>\n<span class=\"token comment\">///         &lt;item>https://github.com/JimBobSquarePants/ImageProcessor/issues/583&lt;/item></span>\n<span class=\"token comment\">///         &lt;item>https://github.com/JimBobSquarePants/ImageProcessor/issues/767&lt;/item></span>\n<span class=\"token comment\">///     &lt;/list></span>\n<span class=\"token comment\">///</span>\n<span class=\"token comment\">///     Code from https://mtsknn.fi/blog/how-to-disable-imageprocessors-upscaling-feature/</span>\n<span class=\"token comment\">/// &lt;/remarks></span>\n<span class=\"token keyword\">private</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">DisableUpscalingByDefault</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">ImageProcessingModule</span> sender<span class=\"token punctuation\">,</span> <span class=\"token class-name\">ValidatingRequestEventArgs</span> args<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>String<span class=\"token punctuation\">.</span><span class=\"token function\">IsNullOrWhiteSpace</span><span class=\"token punctuation\">(</span>args<span class=\"token punctuation\">.</span>QueryString<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token class-name\"><span class=\"token keyword\">var</span></span> queryCollection <span class=\"token operator\">=</span> HttpUtility<span class=\"token punctuation\">.</span><span class=\"token function\">ParseQueryString</span><span class=\"token punctuation\">(</span>args<span class=\"token punctuation\">.</span>QueryString<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// The URL parameter has already been set,</span>\n    <span class=\"token comment\">// so let's not modify it</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>queryCollection<span class=\"token punctuation\">.</span>AllKeys<span class=\"token punctuation\">.</span><span class=\"token function\">Contains</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"upscale\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    queryCollection<span class=\"token punctuation\">.</span><span class=\"token function\">Add</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"upscale\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"false\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    args<span class=\"token punctuation\">.</span>QueryString <span class=\"token operator\">=</span> queryCollection<span class=\"token punctuation\">.</span><span class=\"token function\">ToString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h3 id=\"disable-completely\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#disable-completely\">Disable completely</a></h3>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">/// &lt;summary></span>\n<span class=\"token comment\">///     Disable ImageProcessor's upscaling feature completely.</span>\n<span class=\"token comment\">///     Image URLs' &lt;c>upscale&lt;/c> URL parameter won't have any effect.</span>\n<span class=\"token comment\">/// &lt;/summary></span>\n<span class=\"token comment\">/// &lt;remarks></span>\n<span class=\"token comment\">///     Not possible via configuration:</span>\n<span class=\"token comment\">///     &lt;list type=\"bullet\"></span>\n<span class=\"token comment\">///         &lt;item>https://imageprocessor.org/imageprocessor-web/imageprocessingmodule/#events&lt;/item></span>\n<span class=\"token comment\">///         &lt;item>https://github.com/JimBobSquarePants/ImageProcessor/issues/583&lt;/item></span>\n<span class=\"token comment\">///         &lt;item>https://github.com/JimBobSquarePants/ImageProcessor/issues/767&lt;/item></span>\n<span class=\"token comment\">///     &lt;/list></span>\n<span class=\"token comment\">///</span>\n<span class=\"token comment\">///     Code from https://mtsknn.fi/blog/how-to-disable-imageprocessors-upscaling-feature/</span>\n<span class=\"token comment\">/// &lt;/remarks></span>\n<span class=\"token keyword\">private</span> <span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">DisableUpscaling</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">ImageProcessingModule</span> sender<span class=\"token punctuation\">,</span> <span class=\"token class-name\">ValidatingRequestEventArgs</span> args<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>String<span class=\"token punctuation\">.</span><span class=\"token function\">IsNullOrWhiteSpace</span><span class=\"token punctuation\">(</span>args<span class=\"token punctuation\">.</span>QueryString<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token class-name\"><span class=\"token keyword\">var</span></span> queryCollection <span class=\"token operator\">=</span> HttpUtility<span class=\"token punctuation\">.</span><span class=\"token function\">ParseQueryString</span><span class=\"token punctuation\">(</span>args<span class=\"token punctuation\">.</span>QueryString<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    queryCollection<span class=\"token punctuation\">.</span><span class=\"token function\">Set</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"upscale\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"false\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    args<span class=\"token punctuation\">.</span>QueryString <span class=\"token operator\">=</span> queryCollection<span class=\"token punctuation\">.</span><span class=\"token function\">ToString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Notice the usage of\n<code>queryCollection.Set()</code>\ninstead of\n<code>queryCollection.Add()</code>.</p>\n<h2 id=\"source\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#source\">Source</a></h2>\n<p>Original solution found in the\n<a href=\"https://github.com/JimBobSquarePants/ImageProcessor/issues/583\" class=\"link link-external\">GitHub issue #583</a>.</p>\n","summary":"Disabling the upscaling feature (by default or completely)\ncan be done via ImageProcessor's `ValidatingRequest` event.\nIt's not possible via a config file.","date_published":"2021-03-10T12:00:00+03:00","date_modified":"2021-06-12T12:00:00+03:00","tags":["C#","Episerver"]},{"id":"https://mtsknn.fi/blog/dotnet-refactoring/","url":"https://mtsknn.fi/blog/dotnet-refactoring/","title":"My notes on two nice .NET refactoring videos","content_html":"<p>YouTube recommended me a two-part video series\nthat was fun to watch and gave me a few lightbulb thoughts.</p>\n<h2 id=\"part-1-preparations\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#part-1-preparations\">Part 1: preparations</a></h2>\n<p>YouTube recommended me\n<a href=\"https://www.youtube.com/watch?v=U3QvTaw224o\" class=\"link link-external\">Nick Chapsas's video (part 1) about refactoring some .NET code</a>.\nIt was great.</p>\n<p>In the first part of the two-part video series,\nNick makes the code testable by extracting its dependencies\nand injecting them back via constructor injection.\nThis way the dependencies can be mocked in unit tests.</p>\n<p>Then he creates unit tests for the code before doing any refactoring.\nOtherwise it would be too easy to accidentally modify the logic of the code.</p>\n<h2 id=\"part-2-refactorings\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#part-2-refactorings\">Part 2: refactorings</a></h2>\n<p>In the <a href=\"https://www.youtube.com/watch?v=Yd4GnWeEkIY\" class=\"link link-external\">part 2 video</a>,\nNick does the actual refactoring.\nThree things struck in my mind.</p>\n<h3 id=\"renaming-method-parameters-can-cause-backwards-incompatibility-in-csharp\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#renaming-method-parameters-can-cause-backwards-incompatibility-in-csharp\">Renaming method parameters can cause backwards incompatibility in C#</a></h3>\n<p>One method parameter has a typo in its name.\nIt would be tempting to rename it,\nbut it would be a breaking change to the consumers of the code\n(warranting a major version increment)\nbecause C# supports\n<a href=\"https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments#named-arguments\" class=\"link link-external\">named arguments</a>.\nThat's a good point that I would have missed.</p>\n<h3 id=\"when-are-interfaces-needed\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#when-are-interfaces-needed\">When are interfaces needed?</a></h3>\n<p>Nick creates interfaces for the extracted dependencies\nto make them mockable,\nexpect for one class.\nThat class doesn't need mocking,\nso it doesn't necessarily need an interface.</p>\n<h3 id=\"using-tuples-instead-of-creating-simple-classes\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#using-tuples-instead-of-creating-simple-classes\">Using tuples instead of creating simple classes</a></h3>\n<p>One method needs to return two values.\nYou could create a simple class for the return value,\nbut Nick uses a\n<a href=\"https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples\" class=\"link link-external\">tuple</a>,\nskipping the need for creating that simple class.</p>\n","summary":"YouTube recommended me a two-part video series\nthat was fun to watch and gave me a few lightbulb thoughts.","date_published":"2021-03-06T12:00:00+03:00","tags":["C#","Testing"]},{"id":"https://mtsknn.fi/blog/css-will-change/","url":"https://mtsknn.fi/blog/css-will-change/","title":"Smoother CSS animations with the `will-change` property","content_html":"<p>The <code>will-change</code> property makes the browser render an element with the GPU,\nwhich can reduce jankiness and also allows utilizing sub-pixel rendering.\nBut use the property sparingly.</p>\n<h2 id=\"cpu-vs-gpu\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#cpu-vs-gpu\">CPU vs GPU</a></h2>\n<p>The beginning and ending of CSS animations can sometimes be glitchy\nbecause some property transitions (like <code>transform</code>) are rendered with the GPU,\nwhereas the elements are otherwise rendered with the CPU.</p>\n<p>I learned about the <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/will-change\" class=\"link link-external\"><code>will-change</code> CSS property</a>\nfrom Josh W Comeau's comprehensive article\n<a href=\"https://www.joshwcomeau.com/animation/css-transitions/\" class=\"link link-external\"><em>An Interactive Guide to CSS Transitions</em></a>.\nFrom the <a href=\"https://www.joshwcomeau.com/animation/css-transitions/#hardware-acceleration\" class=\"link link-external\"><em>Hardware acceleration</em> section</a>:</p>\n<blockquote>\n<p>GPUs and CPUs render things <em>slightly</em> differently. [...]</p>\n<p>We can fix this problem by adding the following CSS property:</p>\n<pre aria-label=\"Code block\" class=\"language-css bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token selector\">.btn</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property\">will-change</span><span class=\"token punctuation\">:</span> transform<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p><code>will-change</code> is a property that allows us to hint to the browser that\nwe're going to animate the selected element,\nand that it should optimize for this case.</p>\n<p>In practice,\nwhat this means is that\nthe browser will let the GPU handle this element <em>all the time</em>.\nNo more handing-off between CPU and GPU,\nno more telltale &quot;snapping into place&quot;.</p>\n</blockquote>\n<h2 id=\"sub-pixel-rendering\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#sub-pixel-rendering\">Sub-pixel rendering</a></h2>\n<blockquote>\n<p>There's another benefit to hardware acceleration:\nwe can take advantage of <em>sub-pixel rendering</em>.</p>\n<p>Properties like <code>margin-top</code> can't sub-pixel-render,\nwhich means they need to round to the nearest pixel,\ncreating a stepped, janky effect.\n<code>transform</code>, meanwhile,\ncan smoothly shift between pixels,\nthanks to the GPU's anti-aliasing trickery.</p>\n</blockquote>\n<h2 id=\"use-will-change-sparingly\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#use-will-change-sparingly\">Use <code>will-change</code> sparingly</a></h2>\n<p>Note however that you shouldn't overuse the <code>will-change</code> property,\nlike Josh later mentions.\nIt's also mentioned on the\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/will-change\" class=\"link link-external\"><em><code>will-change</code></em> page on MDN</a>:</p>\n<blockquote>\n<p><strong>Important:</strong> <code>will-change</code> is intended to be used as a last resort,\nin order to try to deal with existing performance problems.\nIt should not be used to anticipate performance problems.</p>\n</blockquote>\n<p>If everything (every animation or transition) is prioritized, nothing is.</p>\n","summary":"The `will-change` property makes the browser render an element with the GPU,\nwhich can reduce jankiness and also allows utilizing sub-pixel rendering.\nBut use the property sparingly.","date_published":"2021-03-06T12:00:00+03:00","tags":["CSS"]},{"id":"https://mtsknn.fi/blog/css-flow-root/","url":"https://mtsknn.fi/blog/css-flow-root/","title":"CSS: `display: flow-root` is better than the clearfix hack","content_html":"<p>The <code>flow-root</code> value is clearer (no hacks!)\nand has great browser support.</p>\n<p><a href=\"https://caniuse.com/flow-root\" class=\"link link-external\"><code>display: flow-root</code> is described on <em>Can I use</em></a>\nlike so:</p>\n<blockquote>\n<p>The element generates a block container box,\nand lays out its contents using flow layout.\nIt always establishes a new block formatting context for its contents.\nIt provides a better solution to the most use cases of the &quot;clearfix&quot; hack.</p>\n</blockquote>\n<p>The browser support of the <code>flow-root</code> value is excellent\n(supported by all modern browsers).\nThat's quite funny,\nbecause I don't remember hearing about this value before.\nI also don't remember where I found it.</p>\n","summary":"The `flow-root` value is clearer (no hacks!)\nand has great browser support.","date_published":"2021-03-06T12:00:00+03:00","tags":["CSS"]},{"id":"https://mtsknn.fi/blog/action-driven-css-animations/","url":"https://mtsknn.fi/blog/action-driven-css-animations/","title":"Action-driven CSS animations","content_html":"<p>Polish your animations by animating based on actions or events,\nlike mouse-enter and mouse-leave,\ninstead of animating based on states,\nlike default and hover state.</p>\n<p>I got the idea from\nJosh W Comeau's comprehensive article\n<a href=\"https://www.joshwcomeau.com/animation/css-transitions/\" class=\"link link-external\"><em>An Interactive Guide to CSS Transitions</em></a>.\nFrom the <a href=\"https://www.joshwcomeau.com/animation/css-transitions/#action-driven-motion\" class=\"link link-external\"><em>Action-driven motion</em> section</a>:</p>\n<blockquote>\n<p>I believe most developers think in terms of <em>states</em>:\nfor example,\nyou might look at this situation\nand say that we have a &quot;hover&quot; state and a default state.\nInstead,\nwhat if we thought in terms of <em>actions</em>?\nWe animate based on what the user is doing,\nthinking in terms of events,\nnot states.\nWe have a mouse-enter animation and a mouse-leave animation.</p>\n</blockquote>\n<h2 id=\"example-modals\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-modals\">Example: modals</a></h2>\n<blockquote>\n<p>Another common example is modals.\nIt can be useful for modals to enter with an <code>ease-out</code> animation,\nand to exit with a quicker <code>ease-in</code> animation.</p>\n<p>This is a small detail,\nbut it speaks to a much larger idea.</p>\n</blockquote>\n<p>Exiting a modal quickly is great\nbecause if the user has closed the modal,\nthey are not interested in it anymore.\nHaving to watch the modal slide away slowly would be pointless.</p>\n","summary":"Polish your animations by animating based on actions or events,\nlike mouse-enter and mouse-leave,\ninstead of animating based on states,\nlike default and hover state.","date_published":"2021-03-06T12:00:00+03:00","tags":["CSS"]},{"id":"https://mtsknn.fi/blog/casting-in-csharp-cast-expression-vs-as-and-is-operators/","url":"https://mtsknn.fi/blog/casting-in-csharp-cast-expression-vs-as-and-is-operators/","title":"Casting in C#: cast expression vs `as` and `is` operators","content_html":"<p>A cast expression like <code>(string)foo</code> throws\nif the types don't match.\nThe <code>as</code> operator doesn't throw\nand defaults to <code>null</code>.\nThe <code>is</code> operator doesn't throw\nand ignores <code>null</code>s.</p>\n<h2 id=\"cast-expression\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#cast-expression\">Cast expression</a></h2>\n<p>Sometimes called &quot;direct casting.&quot;</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token class-name\"><span class=\"token keyword\">var</span></span> bar <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">string</span><span class=\"token punctuation\">)</span>foo<span class=\"token punctuation\">;</span>\n\n    <span class=\"token comment\">// `bar` is a `string` (can be `null`)</span>\n<span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">InvalidCastException</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// `foo` is not a `string`</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<ul>\n<li>If <code>foo</code> is not a <code>string</code>: throws <code>InvalidCastException</code>.</li>\n<li>Otherwise assigns <code>foo</code> to <code>bar</code> (can be <code>null</code>).</li>\n<li>Use when you are sure that the type is correct.</li>\n</ul>\n<h2 id=\"as-operator\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#as-operator\"><code>as</code> operator</a></h2>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token class-name\"><span class=\"token keyword\">var</span></span> bar <span class=\"token operator\">=</span> foo <span class=\"token keyword\">as</span> <span class=\"token class-name\"><span class=\"token keyword\">string</span></span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>bar <span class=\"token operator\">!=</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// `bar` is a `string` and not `null`</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<ul>\n<li>If <code>foo</code> is not a <code>string</code>: assigns <code>null</code> to <code>bar</code>; doesn't throw.</li>\n<li>Otherwise assigns <code>foo</code> to <code>bar</code> (can be <code>null</code>).</li>\n<li>Use when the type <em>might</em> be correct.</li>\n</ul>\n<h2 id=\"is-operator\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#is-operator\"><code>is</code> operator</a></h2>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>foo <span class=\"token keyword\">is</span> <span class=\"token class-name\"><span class=\"token keyword\">string</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// `foo` is a `string` and not `null`</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>foo <span class=\"token keyword\">is</span> <span class=\"token class-name\"><span class=\"token keyword\">string</span></span> bar<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// `bar` is a `string` and not `null`</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<ul>\n<li>If <code>foo</code> is not a <code>string</code> or is <code>null</code>: returns <code>false</code>.</li>\n<li>Otherwise returns <code>true</code>.\n<ul>\n<li>Also in the second example above:\nassigns <code>foo</code> to a new variable <code>bar</code>.</li>\n</ul>\n</li>\n<li>Use in <code>if</code> statements\nto combine the usage of the <code>as</code> operator and <code>null</code> check.</li>\n<li>Use in <code>switch</code> statements for\n<a href=\"https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/switch#type-pattern\" class=\"link link-external\">type pattern matching</a>.</li>\n</ul>\n<h2 id=\"mnemonic-cast-expression-vs-as-operator\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#mnemonic-cast-expression-vs-as-operator\">Mnemonic: cast expression vs <code>as</code> operator</a></h2>\n<p>Use the <code>as</code> operator\nwhen you only <strong>as</strong>-sume<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup> that the type is correct.\nIf your assumption is incorrect,\nthat's okay!\nYou'll get a <code>null</code>\nand no exception will be thrown.</p>\n<p>Conversely,\nif you are sure that the type is correct,\nuse a cast <strong>ex</strong>-pression.\nYou'll be punished with an <strong>ex</strong>-ception\nif you are wrong.</p>\n<h2 id=\"sources-further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#sources-further-resources\">Sources / Further resources</a></h2>\n<ul>\n<li><a href=\"https://stackoverflow.com/q/132445/1079869\" class=\"link link-external\"><em>Direct casting vs 'as' operator?</em> on Stack Overflow</a></li>\n<li><a href=\"https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast#cast-expression\" class=\"link link-external\"><em>Cast expression</em> on Microsoft Docs</a></li>\n<li><a href=\"https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast#as-operator\" class=\"link link-external\"><em><code>as</code> operator</em> on Microsoft Docs</a></li>\n<li><a href=\"https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast#is-operator\" class=\"link link-external\"><em><code>is</code> operator</em> on Microsoft Docs</a></li>\n<li><a href=\"https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast#type-testing-with-pattern-matching\" class=\"link link-external\"><em>Type testing with pattern matching</em> on Microsoft Docs</a></li>\n</ul>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>See\n<a href=\"https://mtsknn.fi/blog/assume-vs-presume-in-english/\" class=\"link\"><em>English: assume vs presume</em></a>. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"A cast expression like `(string)foo` throws\nif the types don't match.\nThe `as` operator doesn't throw\nand defaults to `null`.\nThe `is` operator doesn't throw\nand ignores `null`s.","date_published":"2021-03-03T12:00:00+03:00","date_modified":"2021-06-11T12:00:00+03:00","tags":["C#","Mnemonics"]},{"id":"https://mtsknn.fi/blog/tests-from-user-perspective/","url":"https://mtsknn.fi/blog/tests-from-user-perspective/","title":"Name front-end test cases from user perspective","content_html":"<p>Such naming helps you create tests that resemble the way your users use your app.</p>\n<p>I noticed that\na colleague from another team\nhad named their test cases from user perspective,\nlike this:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">describe</span><span class=\"token punctuation\">(</span><span class=\"token string\">'&lt;FooComponent>'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">test</span><span class=\"token punctuation\">(</span><span class=\"token string\">'user can see all options'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token function\">test</span><span class=\"token punctuation\">(</span><span class=\"token string\">'user can toggle between options'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token comment\">// etc.</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>I was already familiar with\nthe primary guiding principle of\n<a href=\"https://testing-library.com/docs/react-testing-library/intro\" class=\"link link-external\">React Testing Library</a>:</p>\n<blockquote>\n<p>The more your tests resemble the way your software is used,\nthe more confidence they can give you.</p>\n</blockquote>\n<p>But I hadn't thought of naming test cases from this perspective.\nMakes sense, though!\n<strong>Naming test cases from user perspective helps you create good tests</strong> –\ntests that resemble the way your users use your app.</p>\n","summary":"Such naming helps you create tests that resemble the way your users use your app.","date_published":"2021-02-27T12:00:00+03:00","tags":["JavaScript","React","Testing"]},{"id":"https://mtsknn.fi/blog/episerver-automapper/","url":"https://mtsknn.fi/blog/episerver-automapper/","title":"Episerver + AutoMapper is a confusing combo","content_html":"<p>Blocks in Episerver implement the <code>IContent</code> interface only during runtime\nwhich makes using AutoMapper difficult.</p>\n<p>A few weeks ago I started using <a href=\"https://automapper.org/\" class=\"link link-external\">AutoMapper</a>\nin an <a href=\"https://www.episerver.com/\" class=\"link link-external\">Episerver (.NET CMS)</a> project\nto map models to view models semi-automatically.</p>\n<p>My initial feelings: 😻</p>\n<p>It was quite nice until I started creating mappings for <em>blocks</em> (reusable pieces of content).</p>\n<p>My feelings now: 😿😾</p>\n<p>Whereas page models implement Episerver's <code>IContent</code> interface &quot;normally,&quot;\n<a href=\"https://world.episerver.com/blogs/Johan-Bjornfot/Dates1/2012/11/Shared-blocks--IContent/\" class=\"link link-external\">block models implement the <code>IContent</code> interface <em>only during runtime</em></a>.</p>\n<p>This means that\ncreating a mapping from <code>IContent</code> to a block type is not possible.\nAt least not possible in this way that I tried:</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Base view model for pages and blocks</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">interface</span> <span class=\"token class-name\">IContentViewModel</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token return-type class-name\"><span class=\"token keyword\">string</span></span> ContentLink <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token return-type class-name\"><span class=\"token keyword\">string</span></span> ContentType <span class=\"token punctuation\">{</span> <span class=\"token keyword\">get</span><span class=\"token punctuation\">;</span> <span class=\"token keyword\">set</span><span class=\"token punctuation\">;</span> <span class=\"token punctuation\">}</span>\n    <span class=\"token comment\">// etc.</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">IContentProfile</span> <span class=\"token punctuation\">:</span> <span class=\"token type-list\"><span class=\"token class-name\">Profile</span></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">public</span> <span class=\"token function\">IContentProfile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token generic-method\"><span class=\"token function\">CreateMap</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>IContent<span class=\"token punctuation\">,</span> IContentViewModel<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">MyPageViewModel</span> <span class=\"token punctuation\">:</span> <span class=\"token type-list\"><span class=\"token class-name\">IContentViewModel</span></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">MyPageProfile</span> <span class=\"token punctuation\">:</span> <span class=\"token type-list\"><span class=\"token class-name\">Profile</span></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">public</span> <span class=\"token function\">MyPageProfile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token generic-method\"><span class=\"token function\">CreateMap</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>MyPage<span class=\"token punctuation\">,</span> MyPageViewModel<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n            <span class=\"token comment\">// `MyPage` implements `IContent`, so this is ok:</span>\n            <span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">IncludeBase</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>IContent<span class=\"token punctuation\">,</span> IContentViewModel<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">MyBlockViewModel</span> <span class=\"token punctuation\">:</span> <span class=\"token type-list\"><span class=\"token class-name\">IContentViewModel</span></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">MyBlockProfile</span> <span class=\"token punctuation\">:</span> <span class=\"token type-list\"><span class=\"token class-name\">Profile</span></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">public</span> <span class=\"token function\">MyBlockProfile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token generic-method\"><span class=\"token function\">CreateMap</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>MyBlock<span class=\"token punctuation\">,</span> MyBlockViewModel<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n            <span class=\"token comment\">// Error! `MyBlock` doesn't implement `IContent` until runtime,</span>\n            <span class=\"token comment\">// so this doesn't work:</span>\n            <span class=\"token punctuation\">.</span><span class=\"token generic-method\"><span class=\"token function\">IncludeBase</span><span class=\"token generic class-name\"><span class=\"token punctuation\">&lt;</span>IContent<span class=\"token punctuation\">,</span> IContentViewModel<span class=\"token punctuation\">></span></span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>I eventually came up with a solution,\nbut it feels <em>very</em> complex and <em>very</em> verbose.</p>\n<p>I don't know if AutoMapper should be this difficult.\nIf you are working on an Episerver project,\nmy suggestion would be to research alternative mappers or do mappings manually.</p>\n","summary":"Blocks in Episerver implement the `IContent` interface only during runtime\nwhich makes using AutoMapper difficult.","date_published":"2021-02-27T12:00:00+03:00","tags":["C#","Episerver"]},{"id":"https://mtsknn.fi/blog/accessible-test-queries/","url":"https://mtsknn.fi/blog/accessible-test-queries/","title":"Prefer accessible queries with (React) Testing Library","content_html":"<p>Accessible queries take into account the semantic meaning of HTML elements.\nSuch queries help you write more accessible apps.</p>\n<p>Your app probably has lots of text in various kinds of HTML elements:</p>\n<ul>\n<li>headings (<code>&lt;h1&gt;</code> – <code>&lt;h6&gt;</code>),</li>\n<li>paragraphs (<code>&lt;p&gt;</code>),</li>\n<li>form labels (<code>&lt;label&gt;</code>)</li>\n<li>etc.</li>\n</ul>\n<p>What is the semantic meaning of each element?</p>\n<p>Does your app have,\nor should it have,\nmeaningful text in elements without a semantic meaning?\nApart from plain paragraphs,\nprobably not!</p>\n<p><a href=\"https://testing-library.com/\" class=\"link link-external\">Testing Library's</a> methods <code>getByText()</code>, <code>queryByText()</code> and <code>findByText()</code>\nsearch for all elements;\nthey don't care about the elements' semantic meanings.\nBecause most text should be in elements with specific semantic meanings,\n<strong>these methods should be used sparingly</strong>.</p>\n<p>For example,\ndon't look for a button using the <code>getByText()</code> method\nbecause the test could pass\neven if the button was implemented in a hacky way<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup>.\nUse the <code>getByRole()</code> method instead:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">getByRole</span><span class=\"token punctuation\">(</span><span class=\"token string\">'button'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">name</span><span class=\"token operator\">:</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">submit</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">i</span></span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>This ensures that the button you are looking for\nis implemented properly and in an accessible way.</p>\n<p>So:\n<strong>prefer more specific queries than the basic <code>*ByText()</code> methods</strong>.\nSee\n<a href=\"https://testing-library.com/docs/queries/about#priority\" class=\"link link-external\">list of recommended prioritizations of the different queries</a>.</p>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>Think of a <code>&lt;div&gt;</code> with a click handler.\nDon't do it, kids – use a <code>&lt;button&gt;</code>. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"Accessible queries take into account the semantic meaning of HTML elements.\nSuch queries help you write more accessible apps.","date_published":"2021-02-27T12:00:00+03:00","tags":["Accessibility","JavaScript","React","Testing"]},{"id":"https://mtsknn.fi/blog/assume-vs-presume-in-english/","url":"https://mtsknn.fi/blog/assume-vs-presume-in-english/","title":"Assume vs presume in English","content_html":"<p>Both are guesses.\nAssume is weaker (little or no evidence),\npresume is stronger (reasonable evidence).\nBut how to remember which is which?</p>\n<h2 id=\"definitions\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#definitions\">Definitions</a></h2>\n<dl>\n<dt>Assume</dt>\n<dd>Make a guess based on little or no evidence.</dd>\n<dt>Presume</dt>\n<dd>Make an informed guess based on reasonable evidence.</dd>\n</dl>\n<p>Source for the word definitions:\n<a href=\"https://www.merriam-webster.com/words-at-play/assume-vs-presume\" class=\"link link-external\"><em>Are 'Assume' and 'Presume' Synonyms?</em> on Merriam-Webster</a>.</p>\n<h2 id=\"mnemonics\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#mnemonics\">Mnemonics</a></h2>\n<ul>\n<li>When you <strong>ass</strong>-ume something,\nit's just a guess that you have pulled from your <strong>ass</strong>.</li>\n<li>When you <strong>pre</strong>-sume something,\nyou have some <strong>pre</strong>-knowledge or <strong>pre</strong>-information\nto support your guess.</li>\n</ul>\n<p>Source for the mnemonics:\nI pulled them from my ass!</p>\n<h2 id=\"suppose\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#suppose\">Suppose?</a></h2>\n<p>I <em>suppose</em> I should add a third word to the comparison: suppose.\nComing soon™.</p>\n","summary":"Both are guesses.\nAssume is weaker (little or no evidence),\npresume is stronger (reasonable evidence).\nBut how to remember which is which?","date_published":"2021-02-25T12:00:00+03:00","date_modified":"2021-06-11T12:00:00+03:00","tags":["English","Mnemonics"]},{"id":"https://mtsknn.fi/blog/how-to-quickly-count-source-lines-of-code-using-npx-cloc/","url":"https://mtsknn.fi/blog/how-to-quickly-count-source-lines-of-code-using-npx-cloc/","title":"How to quickly count (source) lines of code using `npx cloc`","content_html":"<p>Run <code>npx cloc [options] &lt;files/dirs&gt;</code>.\nE.g. <code>npx cloc src/</code> or <code>npx cloc foo.js bar.css</code>.</p>\n<p>The\n<a href=\"https://github.com/kentcdodds/cloc\" class=\"link link-external\"><code>cloc</code> npm package</a>\nby Kent C. Dodds\nis a wrapper around the\n<a href=\"https://github.com/AlDanial/cloc\" class=\"link link-external\"><code>cloc</code> command line program</a>\nby Al Danial.</p>\n<p>Example from this repo:</p>\n<pre aria-label=\"Code block\" class=\"language-text bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">$ npx cloc . \\\n    --exclude-dir=_sample-content,_site,node_modules \\\n    --not-match-f=package-lock.json\n\nnpx: installed 1 in 1.085s\n     172 text files.\n     162 unique files.\n      49 files ignored.\n\ngithub.com/AlDanial/cloc v 1.90  T=0.22 s (707.6 files/s, 59215.9 lines/s)\n-------------------------------------------------------------------------------\nLanguage                     files          blank        comment           code\n-------------------------------------------------------------------------------\nMarkdown                       100           1947              0           8129\nJavaScript                      27            114            110            848\nPug                             18             78            123            789\nCSS                              1             34             31            328\nTOML                             1             31              2            127\nJSON                             2              0              0             67\nYAML                             1              2              2             12\nXML                              1              0              0              8\nBourne Shell                     1              4              9              7\nSVG                              1              0              0              1\n-------------------------------------------------------------------------------\nSUM:                           153           2210            277          10316\n-------------------------------------------------------------------------------\n</span></code></pre>\n<p>Run <code>npx cloc</code> or <code>npx cloc --help</code> for more options and information.</p>\n","summary":"Run `npx cloc [options] <files/dirs>`.\nE.g. `npx cloc src/` or `npx cloc foo.js bar.css`.","date_published":"2021-02-24T12:00:00+03:00","date_modified":"2021-06-12T12:00:00+03:00","tags":["Cool tools"]},{"id":"https://mtsknn.fi/blog/how-to-exclude-your-own-mobile-browser-visits-from-plausible-analytics/","url":"https://mtsknn.fi/blog/how-to-exclude-your-own-mobile-browser-visits-from-plausible-analytics/","title":"Excluding your own mobile browser visits from Plausible Analytics","content_html":"<p>Use a bookmarklet\nto stop Plausible Analytics\nfrom counting your own visits\nmade using mobile devices.</p>\n<h2 id=\"excluding-desktop-browsers\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#excluding-desktop-browsers\">Excluding desktop browsers</a></h2>\n<p><strong>TL;DR:</strong>\nRun <code>localStorage.plausible_ignore = true</code> in the browser's console\nto start excluding your own visits.</p>\n<p><a href=\"https://plausible.io/docs/excluding-localstorage\" class=\"link link-external\">More specific instructions in Plausible docs.</a></p>\n<h2 id=\"excluding-mobile-browsers\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#excluding-mobile-browsers\">Excluding mobile browsers</a></h2>\n<p>We need to set the <code>localStorage</code> item mentioned in the previous section.</p>\n<p>However,\nrunning code in a browser's console on an Android device is a hassle<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup>.\nRunning code in iOS Safari's console is also a hassle<sup><a aria-label=\"footnote 2\" class=\"link\" href=\"#fn-2\" id=\"fnref-2\">[2]</a></sup>,\nand only possible if you have a Mac as far as I know.</p>\n<h3 id=\"what-doesnt-work\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#what-doesnt-work\">What doesn't work</a></h3>\n<p>You could try pasting the following code to the browser's URL bar\nand then executing it by &quot;visiting&quot; the URL:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token literal-property property\">javascript</span><span class=\"token operator\">:</span>localStorage<span class=\"token punctuation\">.</span>plausible_ignore<span class=\"token operator\">=</span><span class=\"token boolean\">true</span>\n</span></code></pre>\n<p>It's the same code as the code that should be run on desktops,\nbut prefixed with <code>javascript:</code>.</p>\n<p>But it doesn't work on my iPad.\nI don't know about Android devices;\nyou could try.</p>\n<h3 id=\"what-does-work-a-bookmarklet\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#what-does-work-a-bookmarklet\">What does work: a bookmarklet</a></h3>\n<p>A bookmark with the location set to a <code>javascript:</code> URL\nis a <a href=\"https://en.wikipedia.org/wiki/Bookmarklet\" class=\"link link-external\">bookmarklet</a>.\nSo,\ncreate a browser bookmark\nwith the location set to the code from the previous section,\nand then run it.\nHere's how:</p>\n<ul>\n<li><a href=\"https://apple.stackexchange.com/a/74208/406227\" class=\"link link-external\">How to create a bookmarklet in iOS Safari.</a>\nThen run it by &quot;opening&quot; the bookmark/bookmarklet.</li>\n<li><a href=\"https://android.stackexchange.com/a/210701/342608\" class=\"link link-external\">How to create and run a bookmarklet in Android Chrome.</a></li>\n</ul>\n<p>Note that\n<a href=\"https://github.com/mozilla-mobile/firefox-ios/issues/5626\" class=\"link link-external\">Firefox for iOS doesn't support bookmarklets</a>.</p>\n<p>I confirmed that the bookmarklet worked\nby looking at my site's analytics;\nmy own visits from my iPad were not counted anymore.</p>\n<p>You can make the bookmarklet a bit friendlier\nby showing an alert box after setting the <code>localStorage</code> item:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token literal-property property\">javascript</span><span class=\"token operator\">:</span>localStorage<span class=\"token punctuation\">.</span>plausible_ignore<span class=\"token operator\">=</span><span class=\"token boolean\">true</span><span class=\"token operator\">&amp;&amp;</span><span class=\"token function\">alert</span><span class=\"token punctuation\">(</span><span class=\"token string\">'OK'</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Then you don't have to wonder whether the bookmarklet was run or not.</p>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>If you want to run code in a browser's console on an Android device,\nsee these articles for pointers:</p>\n<ul>\n<li><a href=\"https://developers.google.com/web/tools/chrome-devtools/remote-debugging\" class=\"link link-external\"><em>Get Started with Remote Debugging Android Devices</em></a></li>\n<li><a href=\"https://developer.mozilla.org/en-US/docs/Tools/Remote_Debugging/Debugging_Firefox_for_Android_with_WebIDE\" class=\"link link-external\"><em>Debugging Firefox for Android with WebIDE</em></a></li>\n</ul>\n <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></li>\n<li id=\"fn-2\"><p>See\n<a href=\"https://www.browserstack.com/guide/how-to-debug-on-iphone\" class=\"link link-external\"><em>How to Debug on iPhone Safari</em></a>. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-2\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"Use a bookmarklet\nto stop Plausible Analytics\nfrom counting your own visits\nmade using mobile devices.","date_published":"2021-02-17T12:00:00+03:00","date_modified":"2021-06-19T12:00:00+03:00","tags":["Bookmarklets"]},{"id":"https://mtsknn.fi/blog/how-to-get-components-html-without-mounting-in-mithriljs/","url":"https://mtsknn.fi/blog/how-to-get-components-html-without-mounting-in-mithriljs/","title":"How to get component's HTML without mounting in Mithril.js","content_html":"<p>Create a <code>DocumentFragment</code>\nand render the component to it.\nThen there's no need to mount the component to the DOM.</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> App <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function-variable function\">view</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'.foo'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'bar'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> fragment <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">DocumentFragment</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\nm<span class=\"token punctuation\">.</span><span class=\"token function\">render</span><span class=\"token punctuation\">(</span>fragment<span class=\"token punctuation\">,</span> <span class=\"token function\">m</span><span class=\"token punctuation\">(</span>App<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>fragment<span class=\"token punctuation\">.</span>firstChild<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Element object</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>fragment<span class=\"token punctuation\">.</span>firstChild<span class=\"token punctuation\">.</span>outerHTML<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Just the HTML: `&lt;div class=\"foo\">bar&lt;/div>`</span>\n</span></code></pre>\n<ul>\n<li><a href=\"https://flems.io/#0=N4IgtglgJlA2CmIBcBGADGgNCAZhBAzsgNqgB2AhmIkiAHQBWR2AxgPZkAu8XyI7ZApwAEAQQAO44QF5hwADplhwgG4R4AdyTCAFAEoZAPmFgdAcjo42bM5mFmARhQBOZvZkUBfRYoFDhOM4UAObUXDLCZJrCACJsLACuYZwAYkGhPJz6imB0zjxQ8M46gSHJdqYS4np6PmR+bAh0sGzBJenJlhDOQgDCABb4ULVkAPSj0sYAogjJwmwODPAsnHUNTS1tpRlcXT2cA0PCEIKcFPXwbDjCM-DJI+OTwpzOCfBrHASN8M2t7WWZPZ9QawKB0NgJbjOAASABUALIAGQeE2MACkEv5OP14MI4UjtAADAA8UAgKmELFgFAIBGk8lw1gZhiczmJozJKkMhMUIGwBHgCBWEE+fBQABYkOKABwgTwAXWwsBOAGsiEhSCBKNQ+JBsc58HyQAlnLA+P1OJxxAQkOMEmRxCrgnR2GBRnr+gbYAABABMdDQdHF7og+vwdEgZEYzBAnAAnuIaCACCwDeJOHL5Z4gA\" class=\"link link-external\">Test the code on flems.io</a></li>\n<li><a href=\"https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment\" class=\"link link-external\"><code>DocumentFragment</code> on MDN</a></li>\n</ul>\n","summary":"Create a `DocumentFragment`\nand render the component to it.\nThen there's no need to mount the component to the DOM.","date_published":"2021-02-10T12:00:00+03:00","date_modified":"2021-06-12T12:00:00+03:00","tags":["JavaScript","Mithril.js"]},{"id":"https://mtsknn.fi/blog/coding-svg-icons-by-hand/","url":"https://mtsknn.fi/blog/coding-svg-icons-by-hand/","title":"Coding SVG icons by hand","content_html":"<p>Creating icons and simple figures by handcrafting SVG code\nis not as hard as it sounds.</p>\n<p>Aleksandr Hovhannisyan\nhas written a good primer on the topic called\n<a href=\"https://www.aleksandrhovhannisyan.com/blog/svg-tutorial-how-to-code-svg-icons-by-hand/\" class=\"link link-external\"><em>SVG Tutorial: How to Code SVG Icons by Hand</em></a>.</p>\n<p>Here's a quick\n<a href=\"https://en.wikipedia.org/wiki/List_of_Moomin_characters#Moomintroll\" class=\"link link-external\">Moomintroll</a>\nthat I created using my new and refreshed SVG skills:</p>\n<svg viewBox=\"0 0 24 24\" class=\"mx-auto my-8 stroke-current w-1/2\" style=\"stroke-linecap: round; stroke-linejoin: round;\" role=\"img\" aria-label=\"A simple handcrafted SVG icon of Moomintroll.\">\n  <path d=\"M 1 23 s 3 -35.5 7 -16 h 3 s 3 -15 5 3 a 7 6 0 1 1 -7 8\" fill=\"none\" />\n  <circle cx=\"8\" cy=\"12\" r=\"2\" fill=\"none\" stroke-width=\"0.5\" />\n  <circle cx=\"8.5\" cy=\"11.5\" r=\"0.5\" />\n  <circle cx=\"13\" cy=\"12\" r=\"2\" fill=\"none\" stroke-width=\"0.5\" />\n  <circle cx=\"13.5\" cy=\"11.5\" r=\"0.5\" />\n</svg>\n<p>Code:</p>\n<pre aria-label=\"Code block\" class=\"language-xml bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>svg</span>\n  <span class=\"token attr-name\">viewBox</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>0 0 24 24<span class=\"token punctuation\">\"</span></span>\n  <span class=\"token attr-name\">style</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>\n    stroke: currentColor;\n    stroke-linecap: round;\n    stroke-linejoin: round;\n  <span class=\"token punctuation\">\"</span></span>\n<span class=\"token punctuation\">></span></span>\n  <span class=\"token comment\">&lt;!-- Body --></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>path</span>\n    <span class=\"token attr-name\">d</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>\n      M 1 23\n      s 3 -35.5 7 -16\n      h 3\n      s 3 -15 5 3\n      a 7 6 0 1 1 -7 8\n    <span class=\"token punctuation\">\"</span></span>\n    <span class=\"token attr-name\">fill</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>none<span class=\"token punctuation\">\"</span></span>\n  <span class=\"token punctuation\">/></span></span>\n\n  <span class=\"token comment\">&lt;!-- Left eye --></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>circle</span> <span class=\"token attr-name\">cx</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>8<span class=\"token punctuation\">\"</span></span>   <span class=\"token attr-name\">cy</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>12<span class=\"token punctuation\">\"</span></span>   <span class=\"token attr-name\">r</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>2<span class=\"token punctuation\">\"</span></span>   <span class=\"token attr-name\">fill</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>none<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">stroke-width</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>0.5<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>circle</span> <span class=\"token attr-name\">cx</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>8.5<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">cy</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>11.5<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">r</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>0.5<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n\n  <span class=\"token comment\">&lt;!-- Right eye --></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>circle</span> <span class=\"token attr-name\">cx</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>13<span class=\"token punctuation\">\"</span></span>   <span class=\"token attr-name\">cy</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>12<span class=\"token punctuation\">\"</span></span>   <span class=\"token attr-name\">r</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>2<span class=\"token punctuation\">\"</span></span>   <span class=\"token attr-name\">fill</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>none<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">stroke-width</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>0.5<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>circle</span> <span class=\"token attr-name\">cx</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>13.5<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">cy</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>11.5<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">r</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>0.5<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>svg</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<p>I used\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#cubic_b%C3%A9zier_curve\" class=\"link link-external\">smooth cubic Bézier curves</a>\n(not covered in Aleksandr's article)\nwhich required some trial and error.\nBut look how lovely he is!</p>\n<p><a href=\"https://www.aleksandrhovhannisyan.com/blog/svg-tutorial-how-to-code-svg-icons-by-hand/\" class=\"link link-external\">Read Aleksandr's SVG Tutorial and start coding icons by hand!</a></p>\n","summary":"Creating icons and simple figures by handcrafting SVG code\nis not as hard as it sounds.","date_published":"2021-02-05T12:00:00+03:00","date_modified":"2021-10-20T12:00:00+03:00","tags":["SVG"]},{"id":"https://mtsknn.fi/blog/how-to-debounce-events-in-mithriljs/","url":"https://mtsknn.fi/blog/how-to-debounce-events-in-mithriljs/","title":"How to debounce events in Mithril.js","content_html":"<p>Disable auto-redraw with <code>event.redraw = false</code>\nand use a timeout.</p>\n<p>Disabling auto-redraw is mentioned\non the <a href=\"https://mithril.js.org/autoredraw.html\" class=\"link link-external\"><em>Auto-redraw system</em> page in Mithril docs</a>:</p>\n<blockquote>\n<p>Mithril automatically redraws after DOM event handlers that are defined in a Mithril view.\nYou can disable an auto-redraw for specific events by setting <code>e.redraw</code> to <code>false</code>.</p>\n</blockquote>\n<p>Example using an <code>oninput</code> event:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">App</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> timeout\n  <span class=\"token keyword\">let</span> value <span class=\"token operator\">=</span> <span class=\"token string\">'25'</span>\n\n  <span class=\"token keyword\">function</span> <span class=\"token function\">oninput</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">e</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    e<span class=\"token punctuation\">.</span>redraw <span class=\"token operator\">=</span> <span class=\"token boolean\">false</span>\n\n    <span class=\"token comment\">// Update state because we are using a controlled component</span>\n    value <span class=\"token operator\">=</span> e<span class=\"token punctuation\">.</span>currentTarget<span class=\"token punctuation\">.</span>value\n\n    <span class=\"token function\">clearTimeout</span><span class=\"token punctuation\">(</span>timeout<span class=\"token punctuation\">)</span>\n    timeout <span class=\"token operator\">=</span> <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// Do something more here if needed</span>\n\n      <span class=\"token comment\">// Plus probably redraw manually</span>\n      m<span class=\"token punctuation\">.</span><span class=\"token function\">redraw</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1000</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token function-variable function\">view</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">[</span>\n      <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'input[type=text]'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> oninput<span class=\"token punctuation\">,</span> value <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n\n      <span class=\"token comment\">// Updating of this value is debounced</span>\n      <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'p'</span><span class=\"token punctuation\">,</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">sqrt</span><span class=\"token punctuation\">(</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<ul>\n<li><a href=\"https://flems.io/#0=N4IgZglgNgpgziAXAbVAOwIYFsZJAOgCsEAaEAYwHs0AXGWvKtOGgAgEEAHT1gXlYAUASj4A+VsAA6aVq1hsaEHJQCuNabPmsAbhigqYfVgHIATAFZj0jazAq05RdVbUIaTmoEwRUmbNYw+ABOMAAmQRgA7kZgenAw1n6yuvqG-IHkKkEhtAAqGEEA5jA0+CkGif6s5LAFuUowqjQCispqQjayrY1qRvE09W3NWMFhEZEkrACMAAxzHX4AvpWsITRZMr5V2hAwkYiCIrziyJ3+WALGbh40yDQAnpwwvHQAHjQAusaTwC5o12pJuVDIshCQzrILsZON9WABZDA0AAW+DgAEcgs1gUIFlUPjZlmhCdIRlhVLQBKFKJkcLR8AAjSihe6TLicBYgMhULCcaAwIJINAqKBQMjxWCOCDUBCIECmKaIUwAdhAiw+ZCgbgA1jLUCBMDg8FgIMigtBOSAslA8EiaDROHBEAB6J32Thawr4blO42m6AAAVM+Bm+AALD6TUizVB8Ma0ERSCAHk88HByGbODRVR9FkA\" class=\"link link-external\">Test the code on flems.io</a></li>\n<li><a href=\"https://flems.io/#0=N4IgZglgNgpgziAXAbVAOwIYFsZJAOgCsEAaEAYwHs0AXGWvKtOGgAhYztYF5XgAdNK1bZKAV1qJWAJhKDhABwBOEcjCkBGAAxyhrGpRoYoUgBQBKHgD55w1qY518oiWwBU7I0+WqY5-AYAMpTkxjAAyjQqaADmpgDk9PEkfLZ2ngCesFLx5GJKSvTkGclpdnkFRRk5AKrhACKlesIAvua6LYKCTCysAJJoCmJsvBbWqXoA9JOsACoAFhBwrFRiUAAmrABGMKzibJRg+vO7A0NsVFgK1PRsGMsA7jBQUADcttPH9CLsELGw+ggOH2rAe4g2212cHmGEKmy2GREL36g2GK0oVxutDgtlgbBoQJg+y6ejAEnIBOoezQf3Opj8E3SMHwcKUGAePFYYGMcBgZUcMGQzIqhVos1hMRgNHwmBwAF1OcL8qKaOKlJLpQA3YxiPloMrkWCw2aE-amAnA4bmMoWolo3i81Wm4amLAsmDrNkPFIAVi0Wmtek6+r0hRo+SEAmarE1EBgDzMwBENCicCkSdluxarDa1jKwiwCSgGB2UGSrGQ+bshfi+GLpfLmZS8UQ8XaVYLCVpw2QNAyChg3DoAA8aHLyxnsDAUtRuzQUtqoLqpALkJmFW1dOlhHL20HBMHutRegBBBQKTlR4Sx+NmSzcGzRmvrCCa8uV6Ods7DFKTnA5FxaHiHM923VhC2-ec+FYTMch8NRgM3Ktn1fcsazrEtnnLeIDCMEw2xSGs4AUDA0HLAUAkMYwLHMUC7F3DoSTdLBxFoUx1hCMQcFofAtkodYMhSM8FEDEAyEuBRoBgJQkDQNYoDIXlYApCBjzwAAWDREGkAB2EAWhIdApzwfByDgUgKGoOgGEQEB62eRlWBfYji2qbYoBCABrd40GDDDS0c5yFFcqQ-igP4YAAWi2DzyG82wHggdYaHmKQdJ9BRhx8loxJAJSYBUtTbJ9RANA0fS5TIcK0E8hAUCMnA8CwCAUpUKBcvydrbPmFMFDTaYJAUTyYlMjFJma1roAAAWkfAtHwdTxpa+Y2vwZq0CICy+wHPA4HIFQFBoCqWiAA\" class=\"link link-external\">Another example on flems.io</a></li>\n</ul>\n<p>I'm not sure how much I like this solution\nbecause it feels like it's not proper debouncing.\nWhat if a global auto-redraw is triggered elsewhere?\nIt might be a problem in some cases.</p>\n<p>Can you think of a better way to debounce events in Mithril.js?\nLemme know!</p>\n","summary":"Disable auto-redraw with `event.redraw = false`\nand use a timeout.","date_published":"2021-02-04T12:00:00+03:00","date_modified":"2021-10-26T12:00:00+03:00","tags":["JavaScript","Mithril.js"]},{"id":"https://mtsknn.fi/blog/how-to-nest-selectors-in-mithriljs-for-extra-brevity/","url":"https://mtsknn.fi/blog/how-to-nest-selectors-in-mithriljs-for-extra-brevity/","title":"How to nest selectors in Mithril.js for extra brevity","content_html":"<p><code>m.nest('ul &gt; li', 'Item')</code>\ncan be achieved with <code>reduceRight()</code>.</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">m<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">nest</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">selector<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>args</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> tags <span class=\"token operator\">=</span> selector<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">'>'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> innermost <span class=\"token operator\">=</span> <span class=\"token function\">m</span><span class=\"token punctuation\">(</span>tags<span class=\"token punctuation\">.</span><span class=\"token function\">pop</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>args<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> tags<span class=\"token punctuation\">.</span><span class=\"token function\">reduceRight</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">child<span class=\"token punctuation\">,</span> tag</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token function\">m</span><span class=\"token punctuation\">(</span>tag<span class=\"token punctuation\">.</span><span class=\"token function\">trim</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> child<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> innermost<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// Usage:</span>\nm<span class=\"token punctuation\">.</span><span class=\"token function\">nest</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ul.foo > li.bar > a[target=_blank]'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> href <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Link'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// The same as:</span>\n<span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ul.foo'</span><span class=\"token punctuation\">,</span> <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'li.bar'</span><span class=\"token punctuation\">,</span> <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a[target=_blank]'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> href <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Link'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// Output in both cases:</span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>ul</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>foo<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n  </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>li</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>bar<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">target</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>_blank<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>...<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Link</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n  </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>li</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>ul</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<p><a href=\"https://flems.io/#0=N4IgZglgNgpgziAXAbVAOwIYFsZJAOgCsEAaEAYwHs0AXGWvLfNeGgAgF42AKOGWcjUoAnEm3wSMwgOZwAlJwB8bYAB00bNlTRx2NDLM5s+AocPxwADlAg1uAckX256zdt1sIaFsKyUPXFjc+rL4lpSW3HL4NMIQQXJiEvhSsi4abMIwNACuwhohcPhZACY55DAAShDSABZ2rpo85LXQJWIhChzKQSExcQliLW2JjZpePn66jekAvurq7uy1WWBG9vU0lnCIAPS7ZeQA1vhUWLv2C2hLbACClpZGahkAbhAwAO6IPF3KyGNsJgsXQOR7KXTCajSNjKGBYexiewAGS8RzgzhIAKBrFBMLYOSgCLY-wyTUBzBx9hseIwyBWMDAHAAxABdIn2AASlBwGIBmmxIKpEBpyH0MmyHAA+gAjKAYNBHNliYBselrWaIgAi5SO2uOAHFKLzSWwWaMMiz1PM0OomH4crRuCVKOQcjhaPhpZQSgBPMT3SzpEBkEwwQQQagIRAgABMAEZEDGAOwgWYssg2BVR1AgTA4Ri2FbQYMgPJQPCbbZ7XYOyxHaSnbm7LCFuJQAACMfwAAZ8AAWZut6D4FtoIikEA0H2WXDRuDkOKWGiplmzIA\" class=\"link link-external\">Test the code on flems.io</a></p>\n<p>Benefits:</p>\n<ul>\n<li>cleaner</li>\n<li>clearer</li>\n<li>cooler!</li>\n</ul>\n<h2 id=\"source\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#source\">Source</a></h2>\n<p><a href=\"https://github.com/fuzetsu/microh/pull/1\" class=\"link link-external\">Original code on GitHub</a>,\nby <a href=\"https://github.com/fuzetsu\" class=\"link link-external\">fuzetsu (Daniel Loomer)</a>.\nVariables renamed on this page for clarity.</p>\n<p><a href=\"https://gitter.im/mithriljs/mithril.js?at=5c9414758126720abc202d2b\" class=\"link link-external\">Earlier version on Mithril's Gitter room</a>,\nalso by fuzetsu.\nHere the array is looped twice (<code>map()</code> + <code>reduceRight()</code>),\nwhereas in the newer version (on this page)\nthere's only one loop (<code>reduceRight()</code>).</p>\n","summary":"`m.nest('ul > li', 'Item')`\ncan be achieved with `reduceRight()`.","date_published":"2021-02-02T12:00:00+03:00","date_modified":"2021-06-12T12:00:00+03:00","tags":["JavaScript","Mithril.js"]},{"id":"https://mtsknn.fi/blog/lazy-and-greedy-quantifiers-in-regex/","url":"https://mtsknn.fi/blog/lazy-and-greedy-quantifiers-in-regex/","title":"Lazy and greedy quantifiers in regular expressions","content_html":"<p>Greedy quantifiers (the default, e.g. <code>*</code>) match as much as possible.\nLazy quantifiers (e.g. <code>*?</code>) match as little as possible.</p>\n<h2 id=\"greedy-quantifiers\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#greedy-quantifiers\">Greedy quantifiers</a></h2>\n<p>Greedy quantifiers keep looking until the condition is not satisfied.\nThey match as much as possible.</p>\n<p>Quantifiers are greedy by default.</p>\n<h2 id=\"lazy-quantifiers\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#lazy-quantifiers\">Lazy quantifiers</a></h2>\n<p>Lazy quantifiers stop looking when the condition is satisfied.\nThey match as little as possible.</p>\n<p>Quantifiers can be made lazy by appending a question mark.</p>\n<h2 id=\"table-of-quantifiers\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#table-of-quantifiers\">Table of quantifiers</a></h2>\n<table>\n<thead>\n<tr>\n<th>Greedy quantifier</th>\n<th>Lazy quantifier</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>*</code></td>\n<td><code>*?</code></td>\n<td>zero or more</td>\n</tr>\n<tr>\n<td><code>+</code></td>\n<td><code>+?</code></td>\n<td>one or more</td>\n</tr>\n<tr>\n<td><code>?</code></td>\n<td><code>??</code></td>\n<td>zero or one</td>\n</tr>\n<tr>\n<td><code>{n}</code></td>\n<td><code>{n}?</code></td>\n<td>exactly n</td>\n</tr>\n<tr>\n<td><code>{n,}</code></td>\n<td><code>{n,}?</code></td>\n<td>n or more</td>\n</tr>\n<tr>\n<td><code>{n,m}</code></td>\n<td><code>{n,m}?</code></td>\n<td>between n and m</td>\n</tr>\n</tbody>\n</table>\n<h2 id=\"example-getting-the-name-of-an-html-tag\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-getting-the-name-of-an-html-tag\">Example: getting the name of an HTML tag</a></h2>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> html <span class=\"token operator\">=</span> <span class=\"token string\">'&lt;div>foo&lt;/div>'</span>\n\n<span class=\"token comment\">// Greedy</span>\nhtml<span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">&lt;.+></span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span>\n<span class=\"token comment\">//=> '&lt;div>foo&lt;/div>'</span>\n\n<span class=\"token comment\">// Lazy</span>\nhtml<span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">&lt;.+?></span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span>\n<span class=\"token comment\">//=> '&lt;div>'</span>\n</span></code></pre>\n<p>As you can see,\nthe greedy quantifier is too greedy in this case,\nwhereas the lazy quantifier matches just enough but not too much.\nIt's like smart laziness.\nNice!</p>\n<h3 id=\"alternative\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#alternative\">Alternative</a></h3>\n<p>Sometimes an alternative is to use a negated character class:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">html<span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">&lt;[^>]+></span><span class=\"token regex-delimiter\">/</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span>\n<span class=\"token comment\">//=> '&lt;div>'</span>\n</span></code></pre>\n<p>In this case the result is the same as when using the lazy quantifier,\nbut the performance is better because\n<a href=\"https://stackoverflow.com/a/3075532/1079869\" class=\"link link-external\">&quot;the negated character class can only match one way for a given input.&quot;</a></p>\n","summary":"Greedy quantifiers (the default, e.g. `*`) match as much as possible.\nLazy quantifiers (e.g. `*?`) match as little as possible.","date_published":"2021-01-29T12:00:00+03:00","date_modified":"2022-04-10T12:00:00+03:00","tags":["Regular expressions"]},{"id":"https://mtsknn.fi/blog/formatting-lists-in-js/","url":"https://mtsknn.fi/blog/formatting-lists-in-js/","title":"Formatting lists with JavaScript's `Intl.ListFormat`","content_html":"<p>Pass an array of strings\nand get back a string with the array items separated by commas,\nexcept with the last comma replaced with the word &quot;and&quot; or &quot;or.&quot;\nLike &quot;item 1, item 2 and item 3.&quot;</p>\n<h2 id=\"using-locales\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#using-locales\">Using locales</a></h2>\n<p><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat\" class=\"link link-external\"><code>Intl.ListFormat</code></a>\nenables &quot;language-sensitive list formatting,&quot;\nso you can use different locales:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> list <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'Ham'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Spam'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Eggs'</span><span class=\"token punctuation\">]</span>\n\n<span class=\"token comment\">/* en-US */</span>\n\n<span class=\"token keyword\">new</span> <span class=\"token class-name\">Intl<span class=\"token punctuation\">.</span>ListFormat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en-US'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">style</span><span class=\"token operator\">:</span> <span class=\"token string\">'long'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">'conjunction'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">format</span><span class=\"token punctuation\">(</span>list<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Ham, Spam, and Eggs</span>\n\n<span class=\"token keyword\">new</span> <span class=\"token class-name\">Intl<span class=\"token punctuation\">.</span>ListFormat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en-US'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">style</span><span class=\"token operator\">:</span> <span class=\"token string\">'short'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">'disjunction'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">format</span><span class=\"token punctuation\">(</span>list<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Ham, Spam, or Eggs</span>\n\n<span class=\"token comment\">/* en-GB */</span>\n\n<span class=\"token keyword\">new</span> <span class=\"token class-name\">Intl<span class=\"token punctuation\">.</span>ListFormat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en-GB'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">style</span><span class=\"token operator\">:</span> <span class=\"token string\">'long'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">'conjunction'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">format</span><span class=\"token punctuation\">(</span>list<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Ham, Spam and Eggs</span>\n\n<span class=\"token keyword\">new</span> <span class=\"token class-name\">Intl<span class=\"token punctuation\">.</span>ListFormat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en-GB'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">style</span><span class=\"token operator\">:</span> <span class=\"token string\">'short'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">'disjunction'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">format</span><span class=\"token punctuation\">(</span>list<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Ham, Spam or Eggs</span>\n\n<span class=\"token comment\">/* ja-JP */</span>\n\n<span class=\"token keyword\">new</span> <span class=\"token class-name\">Intl<span class=\"token punctuation\">.</span>ListFormat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ja-JP'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">style</span><span class=\"token operator\">:</span> <span class=\"token string\">'long'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">'conjunction'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">format</span><span class=\"token punctuation\">(</span>list<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Ham、Spam、Eggs</span>\n\n<span class=\"token keyword\">new</span> <span class=\"token class-name\">Intl<span class=\"token punctuation\">.</span>ListFormat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ja-JP'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">style</span><span class=\"token operator\">:</span> <span class=\"token string\">'short'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">'disjunction'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">format</span><span class=\"token punctuation\">(</span>list<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Ham、Spam、またはEggs</span>\n</span></code></pre>\n<p>Notice how the <code>en-US</code> locale uses\n<a href=\"https://en.wikipedia.org/wiki/Serial_comma\" class=\"link link-external\">Oxford commas</a>\nand <code>en-GB</code> does not.</p>\n<h2 id=\"formatting-to-parts\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#formatting-to-parts\">Formatting to parts</a></h2>\n<p>There's also a\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat/formatToParts\" class=\"link link-external\"><code>formatToParts()</code> method</a>\nthat &quot;returns an Array of objects representing the different components\nthat can be used to format a list of values in a locale-aware fashion&quot;:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> list <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'Ham'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Spam'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'Eggs'</span><span class=\"token punctuation\">]</span>\n\n<span class=\"token keyword\">new</span> <span class=\"token class-name\">Intl<span class=\"token punctuation\">.</span>ListFormat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en-US'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">style</span><span class=\"token operator\">:</span> <span class=\"token string\">'long'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">'disjunction'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">formatToParts</span><span class=\"token punctuation\">(</span>list<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [</span>\n<span class=\"token comment\">//     { type: 'element', value: 'Ham' },</span>\n<span class=\"token comment\">//     { type: 'literal', value: ', ' },</span>\n<span class=\"token comment\">//     { type: 'element', value: 'Spam' },</span>\n<span class=\"token comment\">//     { type: 'literal', value: ', or ' }, // Oxford comma 👀</span>\n<span class=\"token comment\">//     { type: 'element', value: 'Eggs' },</span>\n<span class=\"token comment\">//   ]</span>\n\n<span class=\"token keyword\">new</span> <span class=\"token class-name\">Intl<span class=\"token punctuation\">.</span>ListFormat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en-GB'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">style</span><span class=\"token operator\">:</span> <span class=\"token string\">'short'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">'disjunction'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">formatToParts</span><span class=\"token punctuation\">(</span>list<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [</span>\n<span class=\"token comment\">//     { type: 'element', value: 'Ham' },</span>\n<span class=\"token comment\">//     { type: 'literal', value: ', ' },</span>\n<span class=\"token comment\">//     { type: 'element', value: 'Spam' },</span>\n<span class=\"token comment\">//     { type: 'literal', value: ' or ' }, // No Oxford comma</span>\n<span class=\"token comment\">//     { type: 'element', value: 'Eggs' },</span>\n<span class=\"token comment\">//   ]</span>\n\n<span class=\"token keyword\">new</span> <span class=\"token class-name\">Intl<span class=\"token punctuation\">.</span>ListFormat</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ja-JP'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">style</span><span class=\"token operator\">:</span> <span class=\"token string\">'short'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">'disjunction'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">formatToParts</span><span class=\"token punctuation\">(</span>list<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [</span>\n<span class=\"token comment\">//     { type: 'element', value: 'Ham' },</span>\n<span class=\"token comment\">//     { type: 'literal', value: '、' },</span>\n<span class=\"token comment\">//     { type: 'element', value: 'Spam' },</span>\n<span class=\"token comment\">//     { type: 'literal', value: '、または' },</span>\n<span class=\"token comment\">//     { type: 'element', value: 'Eggs' },</span>\n<span class=\"token comment\">//   ]</span>\n</span></code></pre>\n<p>I'm <a href=\"https://github.com/mtsknn/mtsknn.fi/pull/29\" class=\"link link-external\">using <code>formatToParts()</code> on this site to format lists of tag links</a>.</p>\n<h2 id=\"browser-support\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#browser-support\">Browser support</a></h2>\n<p><a href=\"https://caniuse.com/mdn-javascript_builtins_intl_listformat\" class=\"link link-external\">Browser support of <code>Intl.Listformat</code></a>\nis nicely green,\nthough Safari and iOS Safari have only recently (April 2021)\nadded support for <code>Intl.ListFormat</code>.</p>\n","summary":"Pass an array of strings\nand get back a string with the array items separated by commas,\nexcept with the last comma replaced with the word \"and\" or \"or.\"\nLike \"item 1, item 2 and item 3.\"","date_published":"2021-01-29T12:00:00+03:00","date_modified":"2021-10-28T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/episerver-tinymce-react/","url":"https://mtsknn.fi/blog/episerver-tinymce-react/","title":"Episerver + TinyMCE + React = uh oh!","content_html":"<p>Thoughts on how to efficiently use React\nto render Episerver blocks\nembedded in TinyMCE fields.</p>\n<p>This week at work\nI was pondering how to mount a React component to a <code>&lt;div&gt;</code>\nwhose contents are defined using the <code>dangerouslySetInnerHTML</code> prop.\nSounds ugly,\nand it is,\nbut hear me out:</p>\n<p><a href=\"https://www.episerver.com/\" class=\"link link-external\">Episerver (.NET CMS)</a>\nuses the <a href=\"https://www.tiny.cloud/\" class=\"link link-external\">TinyMCE editor</a>\nfor rich text editing\nand allows drag-and-dropping &quot;blocks&quot; (reusable pieces of content)\ninto TinyMCE fields.\nBy default,\nblocks are rendered as empty <code>&lt;div&gt;</code>s,\nlike this (simplified example):</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">></span></span>Normal content<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">data-epi-block-id</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>123<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">></span></span>\n  More normal content.\n  Block inside a paragraph:\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">data-epi-block-id</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>456<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n  And more text.\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<p>Now,\nI <em>could</em>:</p>\n<ol>\n<li>render the HTML as-is,</li>\n<li>retrieve the blocks' data using Ajax requests after the component has been mounted,</li>\n<li>and then mount other React components to the <code>&lt;div&gt;</code>s.</li>\n</ol>\n<p>But I don't want to,\nbecause that would be slow\nand incur an extra HTTP request <em>per each block</em>.</p>\n<p>I'm thinking of including the blocks' data in the HTML\nbefore sending it to the front-end:</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">></span></span>Normal content<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">data-epi-block</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">'</span>{ <span class=\"token punctuation\">\"</span>id<span class=\"token punctuation\">\"</span>: 123, <span class=\"token punctuation\">\"</span>blockType<span class=\"token punctuation\">\"</span>: <span class=\"token punctuation\">\"</span>FooBlock<span class=\"token punctuation\">\"</span>, <span class=\"token punctuation\">\"</span>title<span class=\"token punctuation\">\"</span>: <span class=\"token punctuation\">\"</span>Hello block<span class=\"token punctuation\">\"</span>, ... }<span class=\"token punctuation\">'</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">></span></span>\n  More normal content.\n  Block inside a paragraph:\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">data-epi-block</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">'</span>{ <span class=\"token punctuation\">\"</span>id<span class=\"token punctuation\">\"</span>: 456, <span class=\"token punctuation\">\"</span>blockType<span class=\"token punctuation\">\"</span>: <span class=\"token punctuation\">\"</span>BarBlock<span class=\"token punctuation\">\"</span>, <span class=\"token punctuation\">\"</span>title<span class=\"token punctuation\">\"</span>: <span class=\"token punctuation\">\"</span>Another block<span class=\"token punctuation\">\"</span>, ... }<span class=\"token punctuation\">'</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n  And more text.\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<p>Then I could use <code>JSON.parse()</code>\nto turn those JSON attributes into props objects.\nMounting React components to those <code>&lt;div&gt;</code>s\ncould be done as suggested in these Stack Overflow answers:</p>\n<ul>\n<li><a href=\"https://stackoverflow.com/a/44688007/1079869\" class=\"link link-external\"><em>React: using React component inside of dangerouslySetInnerHTML</em></a></li>\n<li><a href=\"https://stackoverflow.com/a/45712853/1079869\" class=\"link link-external\"><em>Render custom React component within HTML string from server</em></a></li>\n</ul>\n<p>Like I said: ugly!\nAnd still just a theory.\nBut necessary.\nWe shall see later if this feasible.</p>\n","summary":"Thoughts on how to efficiently use React\nto render Episerver blocks\nembedded in TinyMCE fields.","date_published":"2021-01-29T12:00:00+03:00","tags":["Episerver","JavaScript","React"]},{"id":"https://mtsknn.fi/blog/how-to-pretty-print-json-in-javascript/","url":"https://mtsknn.fi/blog/how-to-pretty-print-json-in-javascript/","title":"How to pretty print JSON in JavaScript","content_html":"<p>Run <code>JSON.stringify(object, null, 2)</code>.\nAdjust the number for a different level of indentation.</p>\n<p>Example:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> object <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">foo</span><span class=\"token operator\">:</span> <span class=\"token string\">'bar'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">ham</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">spam</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token number\">10</span><span class=\"token punctuation\">,</span> <span class=\"token number\">20</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">stringify</span><span class=\"token punctuation\">(</span>object<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// {\"foo\":\"bar\",\"ham\":{\"spam\":[10,20]}}</span>\n\n<span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">stringify</span><span class=\"token punctuation\">(</span>object<span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// {</span>\n<span class=\"token comment\">//   \"foo\": \"bar\",</span>\n<span class=\"token comment\">//   \"ham\": {</span>\n<span class=\"token comment\">//     \"spam\": [</span>\n<span class=\"token comment\">//       10,</span>\n<span class=\"token comment\">//       20</span>\n<span class=\"token comment\">//     ]</span>\n<span class=\"token comment\">//   }</span>\n<span class=\"token comment\">// }</span>\n</span></code></pre>\n<p><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify\" class=\"link link-external\">Read more about <code>JSON.stringify()</code> on MDN</a></p>\n","summary":"Run `JSON.stringify(object, null, 2)`.\nAdjust the number for a different level of indentation.","date_published":"2021-01-28T12:00:00+03:00","date_modified":"2021-06-12T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/converting-a-path-into-cumulative-segments-in-javascript/","url":"https://mtsknn.fi/blog/converting-a-path-into-cumulative-segments-in-javascript/","title":"3 ways to convert a path into cumulative segments in JavaScript","content_html":"<p>How to convert\n<code>'/foo/bar/baz/'</code>\ninto an array of\n<code>'/'</code>, <code>'/foo/'</code>, <code>'/foo/bar/'</code>, and <code>'/foo/bar/baz/'</code>.\nExample use case: building breadcrumb navigations.</p>\n<h2 id=\"the-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-problem\">The problem</a></h2>\n<p>Given a path like <code>/foo/bar/baz/</code>,\nwe want to convert it into an array like this:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">[</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'/foo/'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'/foo/bar/'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'/foo/bar/baz/'</span><span class=\"token punctuation\">]</span>\n</span></code></pre>\n<p>Starting from the left,\neach array item grows until the next forward slash in the path.\nThe path could be a directory path or an URL pathname.</p>\n<p>I needed this when creating breadcrumb navigations for this website,\nso the paths were URL pathnames.</p>\n<h2 id=\"the-3-solutions\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-3-solutions\">The 3 solutions</a></h2>\n<h3 id=\"split-and-reduce\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#split-and-reduce\">Split and reduce</a></h3>\n<p>This was my initial solution:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  path\n    <span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span> <span class=\"token comment\">// Drop empty strings caused by the splitting</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span>\n      <span class=\"token punctuation\">(</span><span class=\"token parameter\">segments<span class=\"token punctuation\">,</span> segment</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">const</span> previous <span class=\"token operator\">=</span> segments<span class=\"token punctuation\">[</span>segments<span class=\"token punctuation\">.</span>length <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">]</span>\n        segments<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>previous<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>segment<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> segments\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">[</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> pathWithTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz/'</span>\n<span class=\"token keyword\">const</span> pathWithoutTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz'</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['/', '/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithoutTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['/', '/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n</span></code></pre>\n<p>Simplistic, but works.</p>\n<p>One caveat though:\nthe last array item will have a trailing slash\neven if the input string doesn't have a trailing slash.\nThis may or may not be a problem in your case.</p>\n<p>If you want to omit the first array item (the slash):</p>\n<ul>\n<li>set <code>'/'</code> as the default value of <code>previous</code></li>\n<li>change the initial array from <code>['/']</code> to <code>[]</code>.</li>\n</ul>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n<span class=\"token prefix unchanged\"> </span>  path\n<span class=\"token prefix unchanged\"> </span>    <span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix unchanged\"> </span>    <span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span>\n<span class=\"token prefix unchanged\"> </span>    <span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span>\n<span class=\"token prefix unchanged\"> </span>      <span class=\"token punctuation\">(</span><span class=\"token parameter\">segments<span class=\"token punctuation\">,</span> segment</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>        <span class=\"token keyword\">const</span> previous <span class=\"token operator\">=</span> segments<span class=\"token punctuation\">[</span>segments<span class=\"token punctuation\">.</span>length <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">]</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>        <span class=\"token keyword\">const</span> previous <span class=\"token operator\">=</span> segments<span class=\"token punctuation\">[</span>segments<span class=\"token punctuation\">.</span>length <span class=\"token operator\">-</span> <span class=\"token number\">1</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">||</span> <span class=\"token string\">'/'</span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>        segments<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>previous<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>segment<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix unchanged\"> </span>        <span class=\"token keyword\">return</span> segments\n<span class=\"token prefix unchanged\"> </span>      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>      <span class=\"token punctuation\">[</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">]</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>      <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>    <span class=\"token punctuation\">)</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> pathWithTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz/'</span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> pathWithoutTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz'</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithTrailingSlash<span class=\"token punctuation\">)</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token comment\">//=> ['/', '/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token comment\">//=> ['/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithoutTrailingSlash<span class=\"token punctuation\">)</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token comment\">//=> ['/', '/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token comment\">//=> ['/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n</span></span></code></pre>\n<p><a href=\"https://flems.io/#0=N4IgtglgJlA2CmIBcBGADGgNCAZhBAzsgNqgB2AhmIkiAHQBWR2AxgPZkAu8XyI7ZApwAEANxTCAvMIAUABwqcAFgEopAPgA6ZYcIXLtu3XQJzYETjIDkAeisrDRunljcATjIBCbNggpk1GxthABE3NjlheDA5TgBPYSE3CDIAcwJhFgoAVwJ4KGEAIwTleESzC04U1MdjN3zslngZWqMZPNTqLgJMRPhOnk41SXVhYFajTI4hPXrRCDZcqT6B7uIOrs4COgQ05WEAWmEUAF0Jow3B7blcpRkAAwASYDk5hdyAX2fLrg+be4cOkmRnqnGybh0Py25w+mHOxFsVjOQN0gO0AhmogATMt5IpVBpHPolK0TBVLIjAZNnPh3F4fH4AqT6lBGs12v1Nj0VpthqNxiijBiRK94PNFhlpFCCOtOVcdjxUvsjqdhAAfNXCRHnaV0G4EO5PF5vCVfYBQv4A86g8GQuXdVqw4TEE5osgY3zwHZsVLWcS9MhsYScNwUfDVRKwCgGpBWXriaw2HA+GyFChuVMUABe9kBHoQ3t9Vn9wgA7sEQ2HzGlI9GlLH4yhE8m2JmM2ms3YVG7817YD6-ViA0HK+GawQozG42Isc2U2n29nc+jpp7C4PeuXg6Gx6la1P47PbC225nO8uyCBsHkECwqtM+FiAOxIACsAA4QB8Th8gA\" class=\"link link-external\">Test the split and reduce solution on flems.io</a></p>\n<h3 id=\"map-with-a-bit-of-curry\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#map-with-a-bit-of-curry\">Map with a bit of curry</a></h3>\n<p>Who doesn't like curry?\nIt makes chicken taste so much better.\n<a href=\"https://javascript.info/currying-partials\" class=\"link link-external\">Currying</a>\nalso makes this cool solution possible:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">cumulativePath</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">acc</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>acc <span class=\"token operator\">+=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>value<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> path<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token function\">cumulativePath</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> pathWithTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz/'</span>\n<span class=\"token keyword\">const</span> pathWithoutTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz'</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithoutTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n</span></code></pre>\n<p>Wait, what?\nLet's see how it works\nby adding console loggings:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">cumulativePath</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">acc</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> acc<span class=\"token punctuation\">,</span> value <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>acc <span class=\"token operator\">+=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>value<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> path<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token function\">cumulativePath</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/foo/bar/baz'</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> { acc: '/',         value: 'foo' }</span>\n<span class=\"token comment\">//   { acc: '/foo/',     value: 'bar' }</span>\n<span class=\"token comment\">//   { acc: '/foo/bar/', value: 'baz' }</span>\n</span></code></pre>\n<p>The outer function's <code>acc</code> parameter acts as an accumulator,\nlike when using an array reducer.</p>\n<p>We are reassigning the parameter,\nwhich by the way violates the\n<a href=\"https://eslint.org/docs/rules/no-param-reassign\" class=\"link link-external\">ESLint rule <code>no-param-reassign</code></a>.\nThis is not a problem per se,\nbut it's a potential source of confusion.</p>\n<p>We could avoid reassigning the parameter\nby getting rid of the currying\nand creating an <code>acc</code> variable:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">cumulativePath</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">acc</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>  <span class=\"token keyword\">let</span> acc <span class=\"token operator\">=</span> <span class=\"token string\">'/'</span>\n<span class=\"token prefix inserted\">+</span>  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">cumulativePath</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>acc <span class=\"token operator\">+=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>value<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token punctuation\">}</span>\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token keyword\">return</span> path<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span>cumulativePath<span class=\"token punctuation\">)</span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token punctuation\">}</span>\n</span></span></code></pre>\n<p>There's one more oddity:\nthe <code>cumulativePath</code> function's return statement contains an addition assignment.\nThis violates the\n<a href=\"https://eslint.org/docs/rules/no-return-assign\" class=\"link link-external\">ESLint rule <code>no-return-assign</code></a>,\nwhich again is not a problem per se but is confusing.</p>\n<p><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Return_value_and_chaining\" class=\"link link-external\">Assignment operators have return values</a>,\nmeaning that these two are effectively equivalent:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">cumulativePath</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">acc</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>acc <span class=\"token operator\">+=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>value<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">cumulativePath</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">acc</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  acc <span class=\"token operator\">+=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>value<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/</span><span class=\"token template-punctuation string\">`</span></span>\n  <span class=\"token keyword\">return</span> acc\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>With all the magic stripped out,\nhere's the full function:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> acc <span class=\"token operator\">=</span> <span class=\"token string\">'/'</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">cumulativePath</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    acc <span class=\"token operator\">+=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>value<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/</span><span class=\"token template-punctuation string\">`</span></span>\n    <span class=\"token keyword\">return</span> acc\n  <span class=\"token punctuation\">}</span>\n  <span class=\"token keyword\">return</span> path<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span>cumulativePath<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Or maybe in a clearer form:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> acc <span class=\"token operator\">=</span> <span class=\"token string\">'/'</span>\n  <span class=\"token keyword\">return</span> path\n    <span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      acc <span class=\"token operator\">+=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>value<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/</span><span class=\"token template-punctuation string\">`</span></span>\n      <span class=\"token keyword\">return</span> acc\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Now it's quite easy to see what's happening.\nThough it's not as cool anymore, is it?</p>\n<p>This is also very similar to the <a href=\"#split-and-reduce\" class=\"link link-anchor\">split and reduce solution</a>.\nBoth solutions have the same caveat:\nthe last array item will have a trailing slash\neven if the input string doesn't have a trailing slash.</p>\n<p>Here's one way to have a slash as the first item of the resulting array:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">cumulativePath</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">acc</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">value</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>acc <span class=\"token operator\">+=</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>value<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">/</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">concat</span><span class=\"token punctuation\">(</span>\n</span><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>    path<span class=\"token punctuation\">.</span><span class=\"token function\">split</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">filter</span><span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token function\">cumulativePath</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token prefix unchanged\"> </span>  <span class=\"token punctuation\">)</span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token punctuation\">}</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> pathWithTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz/'</span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> pathWithoutTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz'</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithTrailingSlash<span class=\"token punctuation\">)</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token comment\">//=> ['/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token comment\">//=> ['/', '/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithoutTrailingSlash<span class=\"token punctuation\">)</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token comment\">//=> ['/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token comment\">//=> ['/', '/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n</span></span></code></pre>\n<p><a href=\"https://flems.io/#0=N4IgtglgJlA2CmIBcBGADGgNCAZhBAzsgNqgB2AhmIkiAHQAWALmLCNgMYD2ZT8vyECAC+mclRr0AVkU48+A2tzIEmAAgBuKNQF41ACgAOFJgwCUugHxrgAHTJq1y1U4CuYV7BMQN8AAomDLoGFBwcFjrW+hoUsK7wEVGhHGoA1HoABgAkwDFx8MIA9Blm9o4ATvBMruUOxqZ0BIawEEz6AOSF7WZ0eLB85foAQlxcCBRkPWAUhvoc7p7evgGmHV1mpWTC9vbO6hoATMFGgYk2ZU48LvMeXkw+-oHHyWfRsfGvyWmZOXnxRSULpVqrU1MROu0ALp0ZQcEwnBpNFptCE9PoDYajcaTOjTWY3Rb3ZaBNbdDb2bZkXZXMbwOiwLgAcw6WkwajIXDUTHKFHwEDIjLUBC8BAYSHabK0axwo0KACMKOV5RQAF5k6kqWn0pkslBsgDuhS5PL5AqFIrFEs0KGlsoVSoVKvWmw1BC1DOZ7UObI5xt5LTNwooovFkoOtq4yodqvVZGc7p1XoOBqN3P9-MFQZDVsOEajyqdsfYIG4YEM+Hg5WQZE8sGwBHgCA49yughQABYkABWAAcIjEIEo1EEMIIshL8n4TEEIkhwiAA\" class=\"link link-external\">Test the map and curry solution on flems.io</a></p>\n<p>All credit of the cleverness goes to\n<a href=\"https://stackoverflow.com/a/55259065/1079869\" class=\"link link-external\">Nina Scholz's post on Stack Overflow</a>\nwhich I found via\n<a href=\"https://stackoverflow.com/a/55261098/1079869\" class=\"link link-external\">another post on Stack Overflow</a>.</p>\n<h3 id=\"regex-to-the-rescue\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#regex-to-the-rescue\">Regex to the rescue</a></h3>\n<p>If all you have is a hammer,\neverything looks like a nail.\nWhat I mean is that\nthis problem looks like it's <em>made</em> to be solved with a regular expression!</p>\n<p>Basically,\nwhen we encounter a forward slash or the end of the string (<code>(\\/|$)</code>),\nwe want to capture it\nplus everything before that (<code>.*</code>).</p>\n<p>We could try something like this:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> path<span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">.*(\\/|$)</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> pathWithTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz/'</span>\n<span class=\"token keyword\">const</span> pathWithoutTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz'</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['/foo/bar/baz/', '']</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithoutTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['/foo/bar/baz', '']</span>\n</span></code></pre>\n<p>Or a <a href=\"/blog/lazy-and-greedy-quantifiers-in-regex/\" class=\"link\">lazy quantifier</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> path<span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">.*?(\\/|$)</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> pathWithTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz/'</span>\n<span class=\"token keyword\">const</span> pathWithoutTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz'</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['/', 'foo/', 'bar/', 'baz/', '']</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithoutTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['/', 'foo/', 'bar/', 'baz', '']</span>\n</span></code></pre>\n<p>Or a positive lookbehind assertion:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> path<span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=.*)(\\/|$)</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> pathWithTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz/'</span>\n<span class=\"token keyword\">const</span> pathWithoutTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz'</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['/', '/', '/', '/', '']</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithoutTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['/', '/', '/', '']</span>\n</span></code></pre>\n<p>All three regexes fail miserably\nbecause we have to do overlapping matches,\nand this is not how they are done.\nCheck out my previous blog post\n<a href=\"https://mtsknn.fi/blog/how-to-do-overlapping-matches-with-regular-expressions/\" class=\"link\"><em>How to do overlapping matches with regular expressions</em></a>\nfor a walkthrough.</p>\n<p>To do overlapping matches,\nwe have to use <code>matchAll()</code>\nand a positive lookbehind assertion with a capturing group inside of it:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">.</span><span class=\"token function\">matchAll</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=(.*(\\/|$)))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> pathWithTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz/'</span>\n<span class=\"token keyword\">const</span> pathWithoutTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz'</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [</span>\n<span class=\"token comment\">//     [\"\", \"/\", \"/\", index: 1, input: \"/foo/bar/baz/\", groups: undefined],</span>\n<span class=\"token comment\">//     [\"\", \"/foo/\", \"/\", index: 5, input: \"/foo/bar/baz/\", groups: undefined],</span>\n<span class=\"token comment\">//     [\"\", \"/foo/bar/\", \"/\", index: 9, input: \"/foo/bar/baz/\", groups: undefined],</span>\n<span class=\"token comment\">//     [\"\", \"/foo/bar/baz/\", \"/\", index: 13, input: \"/foo/bar/baz/\", groups: undefined],</span>\n<span class=\"token comment\">//   ]</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithoutTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [</span>\n<span class=\"token comment\">//     [\"\", \"/\", \"/\", index: 1, input: \"/foo/bar/baz\", groups: undefined],</span>\n<span class=\"token comment\">//     [\"\", \"/foo/\", \"/\", index: 5, input: \"/foo/bar/baz\", groups: undefined],</span>\n<span class=\"token comment\">//     [\"\", \"/foo/bar/\", \"/\", index: 9, input: \"/foo/bar/baz\", groups: undefined],</span>\n<span class=\"token comment\">//     [\"\", \"/foo/bar/baz\", \"\", index: 12, input: \"/foo/bar/baz\", groups: undefined],</span>\n<span class=\"token comment\">//   ]</span>\n</span></code></pre>\n<p>Looks good.\nLet's get only the second array items,\ni.e. the matches from the capturing group inside the lookbehind assertion:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span>\n    path<span class=\"token punctuation\">.</span><span class=\"token function\">matchAll</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=(.*(\\/|$)))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">(</span><span class=\"token parameter\">match</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> match<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span>\n  <span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> pathWithTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz/'</span>\n<span class=\"token keyword\">const</span> pathWithoutTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz'</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['/', '/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n\n<span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithoutTrailingSlash<span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['/', '/foo/', '/foo/bar/', '/foo/bar/baz']</span>\n</span></code></pre>\n<p>Egg-cellent!\nAs you can see,\nwhether the last item of the resulting array has a trailing slash\ndepends on whether the input string has a trailing slash.\nThis is better than the other two solutions;\nthey always include a trailing slash in the last item of the resulting array.</p>\n<p>If you want to omit the first array item (the slash),\nyou need to only change <code>.*</code> to <code>.+</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getCumulativePathSegments</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">path</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>  Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">.</span><span class=\"token function\">matchAll</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=(.*(\\/|$)))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">match</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> match<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>  Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">.</span><span class=\"token function\">matchAll</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=(.+(\\/|$)))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">match</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> match<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> pathWithTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz/'</span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">const</span> pathWithoutTrailingSlash <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz'</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithTrailingSlash<span class=\"token punctuation\">)</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token comment\">//=> ['/', '/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token comment\">//=> ['/foo/', '/foo/bar/', '/foo/bar/baz/']</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pathWithoutTrailingSlash<span class=\"token punctuation\">)</span>\n</span><span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span><span class=\"token comment\">//=> ['/', '/foo/', '/foo/bar/', '/foo/bar/baz']</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span><span class=\"token comment\">//=> ['/foo/', '/foo/bar/', '/foo/bar/baz']</span>\n</span></span></code></pre>\n<p><a href=\"https://flems.io/#0=N4IgtglgJlA2CmIBcBGADGgNCAZhBAzsgNqgB2AhmIkiAHQBWR2AxgPZkAu8XyI7ZApwAEANxTCAvMIAUABwqcAFgEopAPgA6ZYcICCAJwMUAnnRwG2YeYqV0wilkr2xYMgPQyA-AB5JMugAqGU1NdwAfABIVGPcAcxVMWQdOJzVJdWEUp2IUAF0VbW0BITEAJilZBWV0rR19I1NzS2tqu2znVw9vPwCAahCwqJiVeMTkx1UNLMncgqKyErYEOlg2OJkAcnEksjZhTmN8CDI44QJYCgIlJE2k8S33HDY2dwAjCgN3igAvTZjihwCMt4Kt1lsdsIAO7uA5HWAnM4XK43O5iFCPZ6vD5fD4-dz-QpkQGCEFgjbbMq7faHCjHU7nS7XW73MqYl7fXG-Qkk4ErNYU0RU6Gw2n0pFM1Gs9nYz7ffE8sggbAEeAIFicCBAvjoJAAVgAHCAAL55Y1AA\" class=\"link link-external\">Test the regex solution on flems.io</a></p>\n<h2 id=\"which-solution-is-the-best\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#which-solution-is-the-best\">Which solution is the best?</a></h2>\n<p>The <a href=\"#split-and-reduce\" class=\"link link-anchor\">split and reduce solution</a>\nmight be the most straightforward solution.\nIt's not as elegant as the other two,\nbut at least it's readable.</p>\n<p>The <a href=\"#map-with-a-bit-of-curry\" class=\"link link-anchor\">map and curry solution</a>\nis fancy –\nactually,\nit's so fancy that I still find it quite confusing.\nIt seems to work,\nbut personally I wouldn't use it.</p>\n<p>The <a href=\"#regex-to-the-rescue\" class=\"link link-anchor\">regex solution</a>\nis the shortest solution,\nbut requires the reader to know some regex trickery.\nIt's also the only solution which respects the input string's trailing slash.\nYou could modify the other two solutions to do that too,\nbut they would become more complex and less readable.</p>\n<p>I'm a rebel,\nso I'm using the regex solution on this website. 🤘</p>\n<h2 id=\"bonus-using-for-breadcrumb-navigations\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#bonus-using-for-breadcrumb-navigations\">Bonus: Using for breadcrumb navigations</a></h2>\n<p>Here's a quick example using React\nhow the <code>getCumulativePathSegments()</code> function\ncould be used for generating breadcrumb navigations:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> pageNames <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token string-property property\">'/'</span><span class=\"token operator\">:</span> <span class=\"token string\">'Home'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string-property property\">'/foo/'</span><span class=\"token operator\">:</span> <span class=\"token string\">'Foo'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string-property property\">'/foo/bar/'</span><span class=\"token operator\">:</span> <span class=\"token string\">'BAR'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token string-property property\">'/foo/bar/baz/'</span><span class=\"token operator\">:</span> <span class=\"token string\">'Bäz'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">Breadcrumb</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> pageUrl <span class=\"token operator\">=</span> <span class=\"token string\">'/foo/bar/baz/'</span> <span class=\"token comment\">// or e.g. `window.location.pathname`</span>\n\n  <span class=\"token keyword\">const</span> items <span class=\"token operator\">=</span> <span class=\"token function\">getCumulativePathSegments</span><span class=\"token punctuation\">(</span>pageUrl<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">segment</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">href</span><span class=\"token operator\">:</span> segment<span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">text</span><span class=\"token operator\">:</span> pageNames<span class=\"token punctuation\">[</span>segment<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>nav</span> <span class=\"token attr-name\">aria-label</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>Breadcrumb<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>ul</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n        </span><span class=\"token punctuation\">{</span>items<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">item</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>\n          <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>li</span> <span class=\"token attr-name\">key</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>item<span class=\"token punctuation\">.</span>href<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>item<span class=\"token punctuation\">.</span>href<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>item<span class=\"token punctuation\">.</span>text<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>li</span><span class=\"token punctuation\">></span></span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>ul</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>nav</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Result:</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>nav</span> <span class=\"token attr-name\">aria-label</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>Breadcrumb<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>ul</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>li</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>Home<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>li</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>li</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/foo/<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>Foo<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>li</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>li</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/foo/bar/<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>BAR<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>li</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>li</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/foo/bar/baz/<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>Bäz<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>li</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>ul</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>nav</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n","summary":"How to convert\n`/foo/bar/baz/`\ninto an array of\n`/`, `/foo/`, `/foo/bar/`, and `/foo/bar/baz/`.\nExample use case: building breadcrumb navigations.","date_published":"2021-01-26T12:00:00+03:00","date_modified":"2021-10-26T12:00:00+03:00","tags":["JavaScript","Regular expressions"]},{"id":"https://mtsknn.fi/blog/two-types-of-skills-and-two-types-of-learning/","url":"https://mtsknn.fi/blog/two-types-of-skills-and-two-types-of-learning/","title":"Two types of skills and two types of learning","content_html":"<p>Expiring skills vs permanent skills\nand just-in-time learning vs just-in-case learning.</p>\n<p>TL;DR of Mike Crittenden's blog post\n<a href=\"https://critter.blog/2020/08/14/learning-a-technology-you-dont-need-right-now-is-a-waste-of-time/\" class=\"link link-external\"><em>Learning a technology you don't need right now is a waste of time</em></a>:</p>\n<ul>\n<li>Two types of skills:\n<ul>\n<li>Expiring skills: hard skills, e.g. TypeScript, React, Kubernetes</li>\n<li>Permanent skills: soft skills, e.g. attitude, discipline, charisma</li>\n</ul>\n</li>\n<li>Two types of learning:\n<ul>\n<li>Just-in-time: learn when you need the skill/knowledge</li>\n<li>Just-in-case: learn in case you need the skill/knowledge in the future</li>\n</ul>\n</li>\n<li>Hard skills expire,\nso learning them just-in-case is a waste of time</li>\n<li>Soft skills are permanent and compound over time,\nso learning them just-in-case is wise</li>\n</ul>\n<p>This is why I'm not eager to take courses\nif they are about hard/expiring skills that I know I won't need in the near future.</p>\n<p><a href=\"https://critter.blog/2020/08/14/learning-a-technology-you-dont-need-right-now-is-a-waste-of-time/\" class=\"link link-external\">Read the full blog post by Mike Crittenden</a></p>\n","summary":"Expiring skills vs permanent skills\nand just-in-time learning vs just-in-case learning.","date_published":"2021-01-22T12:00:00+03:00","date_modified":"2021-10-26T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/javascripts-date-constructor-is-bad-at-parsing-dates/","url":"https://mtsknn.fi/blog/javascripts-date-constructor-is-bad-at-parsing-dates/","title":"`new Date()` and `Date.parse()` are bad at parsing dates","content_html":"<p>Use them only with certain date time formats,\nand use a library for parsing fancy formats.</p>\n<h2 id=\"they-used-to-be-discouraged\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#they-used-to-be-discouraged\">They used to be discouraged</a></h2>\n<p>From\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date\" class=\"link link-external\"><em><code>Date()</code> constructor</em> on MDN</a>:</p>\n<blockquote>\n<p>Parsing of date strings with the <code>Date</code> constructor\n(and <code>Date.parse()</code>, which works the same way)\nis <em>strongly discouraged</em> due to browser differences and inconsistencies.</p>\n</blockquote>\n<p><strong>Update:</strong>\n<a href=\"https://github.com/mdn/content/pull/17100\" class=\"link link-external\">the MDN page was updated in June 2022</a>.\nNow the page doesn't say anymore that the <code>Date()</code> constructor and <code>Date.parse()</code> are &quot;strongly discouraged.&quot;\nFrom the linked pull request:</p>\n<blockquote>\n<p>I think parsing ISO date strings is useful enough to not be completely discouraged.</p>\n</blockquote>\n<h2 id=\"fancy-formats-are-poorly-supported\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#fancy-formats-are-poorly-supported\">Fancy formats are poorly supported</a></h2>\n<p>For example,\nthe string <code>&quot;2021-01-10 12:47:29 UTC&quot;</code>\nis parsed correctly on Chrome,\nbut results in &quot;Invalid Date&quot; on Firefox and iOS Safari (as of Jan 22, 2021):</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span><span class=\"token string\">'2021-01-10 12:47:29 UTC'</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Chrome:     Sun Jan 10 2021 14:47:29 GMT+0200 (Eastern European Standard Time)</span>\n<span class=\"token comment\">//   Firefox:    Invalid Date</span>\n<span class=\"token comment\">//   iOS Safari: Invalid Date</span>\n</span></code></pre>\n<h2 id=\"certain-formats-are-well-supported\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#certain-formats-are-well-supported\">Certain formats are well-supported</a></h2>\n<p>Compare with a proper format\nthat works in all three browsers:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span><span class=\"token string\">'2021-01-10T12:47:29.000Z'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// Z = UTC</span>\n<span class=\"token comment\">//=> Chrome:     Sun Jan 10 2021 14:47:29 GMT+0200 (Eastern European Standard Time)</span>\n<span class=\"token comment\">//   Firefox:    Sun Jan 10 2021 14:47:29 GMT+0200 (Eastern European Standard Time)</span>\n<span class=\"token comment\">//   iOS Safari: Sun Jan 10 2021 14:47:29 GMT+0200 (EET)</span>\n</span></code></pre>\n<p>For proper formats,\nsee <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format\" class=\"link link-external\"><em>Date time string format</em> on MDN</a>.</p>\n<h2 id=\"momentjs-vs-others\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#momentjs-vs-others\">Moment.js vs others</a></h2>\n<p>If you have to parse fancy formats,\nconsider using a library for that.</p>\n<p>Probably don't choose Moment.js for new projects\nbecause <a href=\"https://momentjs.com/docs/#/-project-status/\" class=\"link link-external\">Moment.js is in maintenance mode</a>.</p>\n<p>Consider <a href=\"https://momentjs.com/docs/#/-project-status/recommendations/\" class=\"link link-external\">Moment.js alternatives</a> instead.\nI have been satisfied with <a href=\"https://date-fns.org/\" class=\"link link-external\">date-fns</a>.</p>\n","summary":"Use them only with certain date time formats,\nand use a library for parsing fancy formats.","date_published":"2021-01-22T12:00:00+03:00","date_modified":"2023-06-13T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/illegal-invocations-in-js/","url":"https://mtsknn.fi/blog/illegal-invocations-in-js/","title":"\"Illegal invocation\" errors in JavaScript","content_html":"<p>The error is thrown when calling a function\nwhose <code>this</code> keyword isn't referring to the object where it originally did,\ni.e. when the &quot;context&quot; of the function is lost.</p>\n<h2 id=\"example-problems\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-problems\">Example problems</a></h2>\n<p>I encountered the &quot;illegal invocation&quot; error\nwhen calling the destructured <code>abort</code> method of an <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/AbortController\" class=\"link link-external\"><code>AbortController</code></a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> abortController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AbortController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> abort <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> abortController\n\n<span class=\"token function\">abort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> TypeError: Illegal invocation</span>\n</span></code></pre>\n<p>Another case:\ntrying to implement jQuery-like shorthands for\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector\" class=\"link link-external\"><code>document.querySelector</code></a> and <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll\" class=\"link link-external\"><code>document.querySelectorAll</code></a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> $ <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span>querySelector\n<span class=\"token keyword\">const</span> $$ <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span>querySelectorAll\n\n<span class=\"token function\">$</span><span class=\"token punctuation\">(</span><span class=\"token string\">'#foo'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> TypeError: Illegal invocation</span>\n<span class=\"token function\">$$</span><span class=\"token punctuation\">(</span><span class=\"token string\">'.bar'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> TypeError: Illegal invocation</span>\n</span></code></pre>\n<p>(By the way:\nmost, if not all, modern browsers\nhave the <code>$</code> and <code>$$</code> shorthands\nbuilt into the browser's JS console.)</p>\n<h2 id=\"description-of-the-error\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#description-of-the-error\">Description of the error</a></h2>\n<p>&quot;Invocation&quot; is the act invoking a function,\nwhich is the same as calling a function.\nInvoke = call.</p>\n<p>An &quot;illegal invocation&quot; error is thrown when\n<strong>calling a function whose <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this\" class=\"link link-external\"><code>this</code> keyword</a> doesn't refer to the object where it originally did</strong>.\nIn other words,\nthe original &quot;context&quot; of the function is lost.</p>\n<p>Chromium browsers call this error an &quot;illegal invocation.&quot;</p>\n<p>Firefox produces more descriptive error messages:</p>\n<ul>\n<li>\n<blockquote>\n<p>TypeError:\n'abort' called on an object\nthat does not implement interface AbortController.</p>\n</blockquote>\n</li>\n<li>\n<blockquote>\n<p>TypeError:\n'querySelector' called on an object\nthat does not implement interface Document.</p>\n</blockquote>\n</li>\n</ul>\n<p>So does Safari:</p>\n<ul>\n<li>\n<blockquote>\n<p>TypeError:\nCan only call AbortController.abort\non instances of AbortController</p>\n</blockquote>\n</li>\n<li>\n<blockquote>\n<p>TypeError:\nCan only call Document.querySelector\non instances of Document</p>\n</blockquote>\n</li>\n</ul>\n<p>Node.js (v16) produces clearly the best error messages, e.g.:</p>\n<ul>\n<li>\n<blockquote>\n<p>TypeError [ERR_INVALID_THIS]:\nValue of &quot;this&quot; must be of type AbortController</p>\n</blockquote>\n</li>\n</ul>\n<p><a href=\"https://deno.land/\" class=\"link link-external\">Deno</a> uses V8 – the same JS engine as Chromium browsers do –\nso Deno also calls the error an &quot;illegal invocation.&quot;</p>\n<h3 id=\"manual-implementation-of-an-illegal-invocation-check\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#manual-implementation-of-an-illegal-invocation-check\">Manual implementation of an &quot;illegal invocation&quot; check</a></h3>\n<p>For demonstration purposes:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> foo <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function-variable function\">bar</span><span class=\"token operator\">:</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Calling foo.bar; `this` refers to'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span> <span class=\"token operator\">!==</span> foo<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">TypeError</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Illegal invocation 🛑'</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Successfully called foo.bar ✅'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\nfoo<span class=\"token punctuation\">.</span><span class=\"token function\">bar</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Calling foo.bar; `this` refers to foo</span>\n<span class=\"token comment\">//=> Successfully called foo.bar ✅</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> bar <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> foo\n<span class=\"token function\">bar</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Calling foo.bar; `this` refers to window</span>\n<span class=\"token comment\">//=> TypeError: Illegal invocation 🛑</span>\n\n<span class=\"token keyword\">const</span> bar2 <span class=\"token operator\">=</span> foo<span class=\"token punctuation\">.</span>bar\n<span class=\"token function\">bar2</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Calling foo.bar; `this` refers to window</span>\n<span class=\"token comment\">//=> TypeError: Illegal invocation 🛑</span>\n</span></code></pre>\n<p>Notes:</p>\n<ul>\n<li>In real code,\nyou should use better error messages.\n&quot;Illegal invocation&quot; is not clear.</li>\n<li>In <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode\" class=\"link link-external\">strict mode</a>,\n<code>this</code> would be <code>undefined</code> instead of <code>window</code> in the error cases.</li>\n</ul>\n<h3 id=\"why-does-the-this-keyword-change\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#why-does-the-this-keyword-change\">Why does the <code>this</code> keyword change?</a></h3>\n<p>Because the <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this\" class=\"link link-external\"><code>this</code> keyword in JavaScript</a> is confusing!</p>\n<p>The gist is in the difference between <em>method invocations</em> and <em>function invocations</em>.</p>\n<h4>Method invocations</h4>\n<p>A <em>method</em> is a function stored as a property of an object.\nWhen invoking (i.e. calling) a method\nusing the dot notation or square bracket notation,\nthe <code>this</code> keyword is bound to the object:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> foo <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">bar</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> method <span class=\"token operator\">=</span> <span class=\"token string\">'bar'</span>\n\n<span class=\"token comment\">// Method invocations:</span>\nfoo<span class=\"token punctuation\">.</span><span class=\"token function\">bar</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> foo</span>\nfoo<span class=\"token punctuation\">[</span><span class=\"token string\">'bar'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> foo</span>\nfoo<span class=\"token punctuation\">[</span>method<span class=\"token punctuation\">]</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> foo</span>\n</span></code></pre>\n<h4>Function invocations</h4>\n<p>When invoking (i.e. calling) a function that is not the property of an object,\nthe <code>this</code> keyword is:</p>\n<ul>\n<li>bound to the global object (<code>window</code>) in <a href=\"https://developer.mozilla.org/en-US/docs/Glossary/Sloppy_mode\" class=\"link link-external\">sloppy mode</a>.</li>\n<li><code>undefined</code> in <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode\" class=\"link link-external\">strict mode</a>.</li>\n</ul>\n<p>In either mode,\nthe original context is lost\nbecause the <code>this</code> keyword doesn't refer to the object where it originally did:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> bar <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> foo\n<span class=\"token keyword\">const</span> bar2 <span class=\"token operator\">=</span> foo<span class=\"token punctuation\">.</span>bar\n<span class=\"token keyword\">const</span> bar3 <span class=\"token operator\">=</span> foo<span class=\"token punctuation\">[</span><span class=\"token string\">'bar'</span><span class=\"token punctuation\">]</span>\n\n<span class=\"token comment\">// Function invocations:</span>\n<span class=\"token function\">bar</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> window (in sloppy mode) / undefined (in strict mode)</span>\n<span class=\"token function\">bar2</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> window (in sloppy mode) / undefined (in strict mode)</span>\n<span class=\"token function\">bar3</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> window (in sloppy mode) / undefined (in strict mode)</span>\n</span></code></pre>\n<p>As to <em>why</em> the context is lost –\nlet's quote <a href=\"https://www.oreilly.com/library/view/javascript-the-good/9780596517748/\" class=\"link link-external\">Douglas Crockford's book <em>JavaScript: The Good Parts</em></a>\n(1st ed., p. 28; emphasis added):</p>\n<blockquote>\n<p>When a function is not the property of an object,\nthen it is invoked as a function:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">var</span> sum <span class=\"token operator\">=</span> <span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token number\">4</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// sum is 7</span>\n</span></code></pre>\n<p>When a function is invoked with this pattern,\n<code>this</code> is bound to the global object.\n<strong>This was a mistake in the design of the language.</strong></p>\n</blockquote>\n<h4>Sidetrack: arrow functions</h4>\n<p>The quote from the book continues\n(pp. 28–29; text split into paragraphs and code block slightly edited):</p>\n<blockquote>\n<p>Had the language been designed correctly,\nwhen the inner function is invoked,\n<code>this</code> would still be bound to the <code>this</code> variable of the outer function.</p>\n<p>A consequence of this error is that\na method cannot employ an inner function to help it do its work\nbecause the inner function does not share the method's access to the object\nas its <code>this</code> is bound to the wrong value.</p>\n<p>Fortunately, there is an easy workaround.\nIf the method defines a variable and assigns it the value of <code>this</code>,\nthe inner function will have access to <code>this</code> through that variable.\nBy convention, the name of that variable is <code>that</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">var</span> myObject <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">value</span><span class=\"token operator\">:</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">// Augment myObject with a double method</span>\nmyObject<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">double</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">var</span> that <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span> <span class=\"token comment\">// Workaround</span>\n\n  <span class=\"token keyword\">var</span> <span class=\"token function-variable function\">helper</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    that<span class=\"token punctuation\">.</span>value <span class=\"token operator\">=</span> <span class=\"token function\">add</span><span class=\"token punctuation\">(</span>that<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span> that<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">helper</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// Invoke helper as a function</span>\n<span class=\"token punctuation\">}</span>\n\nmyObject<span class=\"token punctuation\">.</span><span class=\"token function\">double</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// Invoke double as a method</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>myObject<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 6</span>\n</span></code></pre>\n</blockquote>\n<p>Nowadays you can alternatively use <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions\" class=\"link link-external\">arrow functions</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">myObject<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">double</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token function-variable function\">helper</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>value <span class=\"token operator\">=</span> <span class=\"token function\">add</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">helper</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// Invoke helper as a function</span>\n<span class=\"token punctuation\">}</span>\n\nmyObject<span class=\"token punctuation\">.</span><span class=\"token function\">double</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// Invoke double as a method</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>myObject<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> 6</span>\n</span></code></pre>\n<p>Arrow functions increase the complexity around the <code>this</code> keyword;\nor reduce complexity, depending on the viewpoint.</p>\n<p>Anyhow, the <code>this</code> keyword in JavaScript is confusing.\nI personally try to avoid it.\nIt has many potential pitfalls,\nand often there are better alternatives.</p>\n<h2 id=\"three-ways-to-fix-the-error\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#three-ways-to-fix-the-error\">Three ways to fix the error</a></h2>\n<p>Here's the original, problematic example code:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> abortController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AbortController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> abort <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> abortController\n\n<span class=\"token keyword\">const</span> $ <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span>querySelector\n<span class=\"token keyword\">const</span> $$ <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span>querySelectorAll\n</span></code></pre>\n<p>The gist of the problem is that\ncalling <code>abort</code>, <code>$</code> or <code>$$</code>\nis a function invocation,\nnot a method invocation,\nso the context is lost.</p>\n<h3 id=\"create-a-function-that-calls-a-method\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#create-a-function-that-calls-a-method\">Create a function that calls a method</a></h3>\n<p>As we learned above,\nwith a method invocation (as opposed to a function invocation),\nthe <code>this</code> keyword is bound to the object.</p>\n<p>So, create an <code>abort</code> function that calls the <code>abortController.abort</code> method:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> abortController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AbortController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">abort</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> abortController<span class=\"token punctuation\">.</span><span class=\"token function\">abort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token function\">abort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK!</span>\n</span></code></pre>\n<p>Calling <code>abort</code> is a function invocation,\nbut <code>abort</code> in turn calls <code>abortController.abort</code> using method invocation,\nso the context is not lost.</p>\n<p>Similarly for <code>$</code> and <code>$$</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">$</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">selectors</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> document<span class=\"token punctuation\">.</span><span class=\"token function\">querySelector</span><span class=\"token punctuation\">(</span>selectors<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> <span class=\"token function-variable function\">$$</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">selectors</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> document<span class=\"token punctuation\">.</span><span class=\"token function\">querySelectorAll</span><span class=\"token punctuation\">(</span>selectors<span class=\"token punctuation\">)</span>\n\n<span class=\"token function\">$</span><span class=\"token punctuation\">(</span><span class=\"token string\">'#foo'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK!</span>\n<span class=\"token function\">$$</span><span class=\"token punctuation\">(</span><span class=\"token string\">'.bar'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK!</span>\n</span></code></pre>\n<p>(By the way:\nnotice how the function parameters are in the plural form:\n<code>selectors</code> instead of <code>selector</code>.\nThat's because <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector\" class=\"link link-external\"><code>document.querySelector</code></a> and <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll\" class=\"link link-external\"><code>document.querySelectorAll</code></a>\naccept a comma-separated list of CSS selectors.)</p>\n<h3 id=\"use-bind-to-change-the-this-keyword\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#use-bind-to-change-the-this-keyword\">Use <code>bind()</code> to change the <code>this</code> keyword</a></h3>\n<p>A more convoluted solution is to use <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind\" class=\"link link-external\"><code>Function.prototype.bind()</code></a>\nto set the <code>this</code> keyword to point to the correct object:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> abortController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AbortController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> abort <span class=\"token operator\">=</span> abortController<span class=\"token punctuation\">.</span><span class=\"token function\">abort</span><span class=\"token punctuation\">.</span><span class=\"token function\">bind</span><span class=\"token punctuation\">(</span>abortController<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> $ <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">querySelector</span><span class=\"token punctuation\">.</span><span class=\"token function\">bind</span><span class=\"token punctuation\">(</span>document<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> $$ <span class=\"token operator\">=</span> document<span class=\"token punctuation\">.</span><span class=\"token function\">querySelectorAll</span><span class=\"token punctuation\">.</span><span class=\"token function\">bind</span><span class=\"token punctuation\">(</span>document<span class=\"token punctuation\">)</span>\n\n<span class=\"token function\">abort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK!</span>\n\n<span class=\"token function\">$</span><span class=\"token punctuation\">(</span><span class=\"token string\">'#foo'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK!</span>\n<span class=\"token function\">$$</span><span class=\"token punctuation\">(</span><span class=\"token string\">'.bar'</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK!</span>\n</span></code></pre>\n<p>There's also <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply\" class=\"link link-external\"><code>Function.prototype.apply()</code></a> and <a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call\" class=\"link link-external\"><code>Function.prototype.call()</code></a>,\nbut they are also convoluted\nbecause they deal with the <code>this</code> keyword.</p>\n<p>I recommend the previous solution which doesn't deal with the <code>this</code> parameter:\n<a href=\"#create-a-function-that-calls-a-method\" class=\"link link-anchor\">Create a function that calls a method</a>.</p>\n<h3 id=\"export-the-whole-object\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#export-the-whole-object\">Export the whole object</a></h3>\n<p>(Maybe an obvious solution,\nbut mentioning it anyway.)</p>\n<p>In the <code>AbortController</code> case,\nI originally destructured the <code>abort</code> method\nbecause I wanted to export only that method,\nnot the whole <code>AbortController</code>.</p>\n<p>If you are fine with exporting the whole <code>AbortController</code>,\ncalling its <code>abort</code> method directly is fine too\n(because it'll be a method invocation):</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">export</span> <span class=\"token keyword\">const</span> abortController <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">AbortController</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// In another file:</span>\n\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> abortController <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'...'</span>\nabortController<span class=\"token punctuation\">.</span><span class=\"token function\">abort</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token comment\">// OK!</span>\n</span></code></pre>\n<p>This solution doesn't apply to the <code>$</code> and <code>$$</code> functions\nbecause <code>document</code> is anyway available in all modules.</p>\n<h2 id=\"sources-further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#sources-further-resources\">Sources / Further resources</a></h2>\n<p>I learned about &quot;illegal invocation&quot; errors via these Stack Overflow questions:</p>\n<ul>\n<li><a href=\"https://stackoverflow.com/q/9677985/1079869\" class=\"link link-external\"><em>&quot;Uncaught TypeError: Illegal invocation&quot; in Chrome</em></a></li>\n<li><a href=\"https://stackoverflow.com/q/10743596/1079869\" class=\"link link-external\"><em>Why are certain function calls termed &quot;illegal invocations&quot; in JavaScript?</em></a></li>\n<li><a href=\"https://stackoverflow.com/q/8904782/1079869\" class=\"link link-external\"><em>Uncaught TypeError: Illegal invocation in JavaScript</em></a></li>\n</ul>\n<p>I learned about the differences between method invocations and function invocations from\n<a href=\"https://www.oreilly.com/library/view/javascript-the-good/9780596517748/\" class=\"link link-external\">Douglas Crockford's book <em>JavaScript: The Good Parts</em></a>.\nChapter 4, &quot;Functions,&quot; has more details,\nand also describes two other invocation patterns in JavaScript:</p>\n<ul>\n<li>the constructor invocation pattern</li>\n<li>the apply invocation pattern.</li>\n</ul>\n<p>Lastly,\nsee also my theoretical musings about\n<a href=\"/blog/illegal-invocations-in-ts/\" class=\"link\">preventing &quot;illegal invocation&quot; errors in TypeScript</a>.</p>\n","summary":"The error is thrown when calling a function\nwhose `this` keyword isn't referring to the object where it originally did,\ni.e. when the \"context\" of the function is lost.","date_published":"2021-01-22T12:00:00+03:00","date_modified":"2022-04-17T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/dynamic-tag-names-in-pug/","url":"https://mtsknn.fi/blog/dynamic-tag-names-in-pug/","title":"Dynamic tag names in Pug","content_html":"<p>Use string interpolation at the beginning of a line,\ne.g. <code>#{myVariable}</code>.</p>\n<p>Example:</p>\n<pre aria-label=\"Code block\" class=\"language-pug bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">-</span><span class=\"token code\"> <span class=\"token keyword\">const</span> tag <span class=\"token operator\">=</span> <span class=\"token string\">'article'</span></span>\n\n#{tag}\n#{tag}<span class=\"token punctuation\">.</span>foo\n#{tag}(class<span class=\"token punctuation\">=</span>\"foo\")\n#{tag}<span class=\"token punctuation\">.</span>foo(class<span class=\"token punctuation\">=</span>\"bar\")\n#{tag}(class<span class=\"token punctuation\">=</span>\"foo\")<span class=\"token punctuation\">.</span>bar\n\n<span class=\"token comment\">// Output:</span>\n<span class=\"token markup\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>article</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>article</span><span class=\"token punctuation\">></span></span></span>\n<span class=\"token markup\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>article</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>foo<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>article</span><span class=\"token punctuation\">></span></span></span>\n<span class=\"token markup\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>article</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>foo<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>article</span><span class=\"token punctuation\">></span></span></span>\n<span class=\"token markup\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>article</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>foo bar<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>article</span><span class=\"token punctuation\">></span></span></span>\n<span class=\"token markup\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>article</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>foo bar<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>article</span><span class=\"token punctuation\">></span></span></span>\n</span></code></pre>\n<p>Works even inside <a href=\"https://pugjs.org/language/interpolation.html#tag-interpolation\" class=\"link link-external\">tag interpolation</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-pug bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">-</span><span class=\"token code\"> <span class=\"token keyword\">const</span> tag <span class=\"token operator\">=</span> <span class=\"token string\">'em'</span></span>\n\n<span class=\"token tag\">p</span> <span class=\"token plain-text\">Bacon #[#{tag}.foo ipsum]!</span>\n\n<span class=\"token comment\">// Output:</span>\n<span class=\"token markup\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">></span></span>Bacon <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>em</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>foo<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>ipsum<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>em</span><span class=\"token punctuation\">></span></span>!<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span></span>\n</span></code></pre>\n<p>Dynamic tag names allow you to use fancy, probably invalid tag names.\nLike these:</p>\n<pre aria-label=\"Code block\" class=\"language-pug bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">#{':foo'} bar\n#{'^'} baz\n\n<span class=\"token comment\">// Output:</span>\n<span class=\"token markup\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>:foo</span><span class=\"token punctuation\">></span></span>bar<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>:foo</span><span class=\"token punctuation\">></span></span></span>\n<span class=\"token markup\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>^</span><span class=\"token punctuation\">></span></span>baz<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>^</span><span class=\"token punctuation\">></span></span></span>\n\n<span class=\"token comment\">// Compare with:</span>\n\n:foo bar\n<span class=\"token comment\">// Error: unknown filter \":foo\"</span>\n\n^ baz\n<span class=\"token comment\">// Error: unexpected text \"^ baz\"</span>\n</span></code></pre>\n<p>Not sure why you would want that though!</p>\n<p><a href=\"https://pugjs.org/language/interpolation.html\" class=\"link link-external\">Read more about interpolations in Pug</a></p>\n","summary":"Use string interpolation at the beginning of a line,\ne.g. `#{myVariable}`.","date_published":"2021-01-22T12:00:00+03:00","date_modified":"2021-08-27T12:00:00+03:00","tags":["Pug"]},{"id":"https://mtsknn.fi/blog/mnemonic-for-array-shift-vs-array-unshift-in-javascript/","url":"https://mtsknn.fi/blog/mnemonic-for-array-shift-vs-array-unshift-in-javascript/","title":"Mnemonic for `array.shift()` vs `array.unshift()` in JavaScript","content_html":"<p><code>shift()</code> removes the first array item,\nand <code>unshift()</code> prepends items to the array.\nBut how to remember which is which?</p>\n<p>First, you need to remember <code>pop()</code> vs <code>push()</code>.\nIf you don't, try these mnemonics:</p>\n<ul>\n<li><code>pop()</code> removes the last array item\nby popping it like a bottle cap.</li>\n<li><code>push()</code> appends items to the array\nby... eh, pushing stuff to it?</li>\n</ul>\n<p>I think they are quite intuitive.</p>\n<p>All right,\nnow the mnemonic for <code>shift()</code> vs <code>unshift()</code>\n(and incidentally also for <code>pop()</code> vs <code>push()</code>):</p>\n<blockquote>\n<p>The method with the <strong>shorter</strong> name <strong>removes</strong> an item.</p>\n</blockquote>\n<p>So, <code>pop()</code> and <code>push()</code>:</p>\n<ul>\n<li><code>pop()</code> has a <strong>shorter</strong> name than <code>push()</code>.</li>\n<li><code>pop()</code> <strong>removes</strong> the last array item.</li>\n<li><code>push()</code> appends items to the array.</li>\n</ul>\n<p>Similarly, <code>shift()</code> vs <code>unshift()</code>:</p>\n<ul>\n<li><code>shift()</code> has a <strong>shorter</strong> name than <code>unshift()</code>.</li>\n<li><code>shift()</code> <strong>removes</strong> the first array item.</li>\n<li><code>unshift()</code> prepends items to the array.</li>\n</ul>\n","summary":"`shift()` removes the first array item,\nand `unshift()` prepends items to the array.\nBut how to remember which is which?","date_published":"2021-01-21T12:00:00+03:00","date_modified":"2021-06-12T12:00:00+03:00","tags":["JavaScript","Mnemonics"]},{"id":"https://mtsknn.fi/blog/using-non-default-locales-with-tolocalestring-in-nodejs/","url":"https://mtsknn.fi/blog/using-non-default-locales-with-tolocalestring-in-nodejs/","title":"Using non-default locales with `toLocaleString()` in Node.js","content_html":"<p>With Node.js 13+,\nit should just work.\nWith earlier Node.js versions,\nyou need to install and use the <code>full-icu</code> package.</p>\n<h2 id=\"the-problem\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-problem\">The problem</a></h2>\n<p>Using Node.js version 12 or earlier,\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString\" class=\"link link-external\"><code>Date.prototype.toLocaleString()</code></a>\nand\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString\" class=\"link link-external\"><code>Number.prototype.toLocaleString()</code></a>\ndon't always produce correct results,\nat least with non-English (or non-default) locales.\nFor example:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> date <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span><span class=\"token string\">'2021-11-22'</span><span class=\"token punctuation\">)</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>date<span class=\"token punctuation\">.</span><span class=\"token function\">toLocaleString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Actual/expected: 11/22/2021, 2:00:00 AM (🆗)</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>date<span class=\"token punctuation\">.</span><span class=\"token function\">toLocaleString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fi'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Actual:   2021-11-22 2:00:00 (❗)</span>\n<span class=\"token comment\">//=> Expected: 22.11.2021 klo 2.00.00</span>\n\n<span class=\"token keyword\">const</span> number <span class=\"token operator\">=</span> <span class=\"token number\">1234.56</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>number<span class=\"token punctuation\">.</span><span class=\"token function\">toLocaleString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'en'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">style</span><span class=\"token operator\">:</span> <span class=\"token string\">'currency'</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">currency</span><span class=\"token operator\">:</span> <span class=\"token string\">'USD'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Actual/expected: $1,234.56 (🆗)</span>\n\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>number<span class=\"token punctuation\">.</span><span class=\"token function\">toLocaleString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fi'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">style</span><span class=\"token operator\">:</span> <span class=\"token string\">'currency'</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">currency</span><span class=\"token operator\">:</span> <span class=\"token string\">'USD'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> Actual:   US$ 1,234.56 (❗)</span>\n<span class=\"token comment\">//=> Expected: 1 234,56 $</span>\n</span></code></pre>\n<h2 id=\"the-solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-solution\">The solution</a></h2>\n<p>The results depend on the ICU data (= localization data) used by Node.js.\nFrom <a href=\"http://site.icu-project.org/\" class=\"link link-external\">site.icu-project.org</a>:</p>\n<blockquote>\n<p>ICU is a mature, widely used set of [...] libraries\nproviding Unicode and Globalization support for software applications.\nICU is widely portable\nand gives applications the same results on all platforms [...].</p>\n</blockquote>\n<h3 id=\"nodejs-13\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#nodejs-13\">Node.js 13+</a></h3>\n<p>Starting from version 13.0.0,\nNode.js comes with full ICU support by default,\nso <code>toLocaleString()</code> should produce correct results.</p>\n<p>From\n<a href=\"https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V13.md#2019-10-22-version-1300-current-bethgriggs\" class=\"link link-external\">version 13.0.0 changelog</a>:</p>\n<blockquote>\n<p>Node.js releases are now built with default full-icu support.\nThis means that all locales supported by ICU are now included\nand Intl-related APIs may return different values than before\n(Richard Lau)\n<a href=\"https://github.com/nodejs/node/pull/29887\" class=\"link link-external\">#29887</a>.</p>\n</blockquote>\n<p><a href=\"https://github.com/nodejs/node/issues/19214\" class=\"link link-external\">Issue #19214</a>\nhas also relevant discussion about this.</p>\n<p><strong>Note:</strong>\nversion 13 is not an LTS (long-time support) version.\nEven-numbered versions – 14, 16, 18 and so on –\nare LTS versions and thus a better choice.\nSee <a href=\"https://nodejs.org/en/about/releases/\" class=\"link link-external\">Node.js Releases</a>.</p>\n<h3 id=\"nodejs-12-and-earlier\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#nodejs-12-and-earlier\">Node.js 12 and earlier</a></h3>\n<p>You need to install and enable full ICU data manually:</p>\n<ol>\n<li>\n<p>Run <code>npm install full-icu cross-env</code>\nto install the following packages:</p>\n<ul>\n<li><a href=\"https://www.npmjs.com/package/full-icu\" class=\"link link-external\"><code>full-icu</code></a>:\nthe full ICU data</li>\n<li><a href=\"https://www.npmjs.com/package/cross-env\" class=\"link link-external\"><code>cross-env</code></a>:\nneeded by Windows users to set the environment variable in the next step.</li>\n</ul>\n</li>\n<li>\n<p>Update the scripts section of <code>package.json</code>\nto set the environment variable <code>NODE_ICU_DATA</code>.\nFor example:</p>\n<pre aria-label=\"Code block\" class=\"language-json bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"scripts\"</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// Before</span>\n    <span class=\"token property\">\"start\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"react-scripts start\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"test\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"react-scripts test\"</span><span class=\"token punctuation\">,</span>\n\n    <span class=\"token comment\">// After</span>\n    <span class=\"token property\">\"start\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"react-scripts start\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token property\">\"test\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"cross-env NODE_ICU_DATA=node_modules/full-icu react-scripts test\"</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n</li>\n</ol>\n<p>Now when you run <code>npm test</code>,\nNode.js will load the full ICU data from <code>node_modules/full-icu</code>,\nand <code>toLocaleString()</code> should produce correct results.</p>\n<p>In the example above,\nyou could also modify the <code>start</code> script,\nbut it might be unnecessary;\ndepends on what the script does.\nIn this case the script opens a browser,\nso modifying the script would be unnecessary.</p>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<p>The same info can be found in\n<a href=\"https://stackoverflow.com/a/56624712/1079869\" class=\"link link-external\">my Stack Overflow answer</a>.</p>\n","summary":"With Node.js 13+,\nit should just work.\nWith earlier Node.js versions,\nyou need to install and use the `full-icu` package.","date_published":"2021-01-15T12:00:00+03:00","date_modified":"2021-06-12T12:00:00+03:00","tags":["JavaScript","Node.js"]},{"id":"https://mtsknn.fi/blog/my-new-microphone-rode-procaster/","url":"https://mtsknn.fi/blog/my-new-microphone-rode-procaster/","title":"My new microphone: RØDE Procaster","content_html":"<p>Much better than a Jabra headset,\nbut also much pricier.\nWhy a dynamic XLR microphone\ninstead of a condenser USB microphone?\nRead on to find out.</p>\n<h2 id=\"huge-upgrade-from-a-jabra-headset\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#huge-upgrade-from-a-jabra-headset\">Huge upgrade from a Jabra headset</a></h2>\n<p>I have previously used a <a href=\"https://www.jabra.com/business/office-headsets/jabra-evolve/jabra-evolve-65\" class=\"link link-external\">Jabra Evolve 65 headset</a>.\nMy new <a href=\"https://www.rode.com/microphones/procaster\" class=\"link link-external\">RØDE Procaster microphone</a>\nhas so much better audio quality.</p>\n<p>I always thought that the Jabra has quite good audio quality –\nand it kind of has; everything is relative.</p>\n<p>But then I compared my voice recorded with the Jabra\nto <a href=\"https://hanki.dev/\" class=\"link link-external\">my brother's</a> voice recorded with a decent condenser microphone.\nThe difference in audio quality was <em>immense</em>.\nNo way I'm going back to the Jabra\n(unless I need portability).</p>\n<p>I have worked mostly remotely for the past few years\nand plan to continue working remotely,\nso having high quality audio equipment benefits my colleagues as well.</p>\n<p>Another reason to buy a decent microphone was that\nmy brother and I were planning to start a podcast.\n(Nowadays our <a href=\"https://koodikrapula.fi/\" class=\"link link-external\">Finnish podcast <em>Koodikrapula</em></a> is up and running.)</p>\n<p>And finally,\n<a href=\"https://tips.ariyh.com/p/good-sound-quality-smarter\" class=\"link link-external\">high-quality audio makes you sound smarter</a>!\n(The linked article was published a few months after I had already bought the microphone,\nso it didn't affect my purchasing decision.\nCool nonetheless. 🤙)</p>\n<h2 id=\"what-did-it-cost\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#what-did-it-cost\">What did it cost?</a></h2>\n<p><em>Everything.</em> :thanos:</p>\n<p>No but seriously,\nthese are the prices I paid in Finland in January 2021:</p>\n<table>\n<thead>\n<tr>\n<th>Product</th>\n<th>Price</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"https://www.rode.com/microphones/procaster\" class=\"link link-external\">RØDE Procaster (microphone)</a></td>\n<td>179€</td>\n</tr>\n<tr>\n<td><a href=\"https://focusrite.com/en/audio-interface/scarlett/scarlett-solo\" class=\"link link-external\">Focusrite Scarlett Solo 3rd Gen (USB audio interface)</a></td>\n<td>110€</td>\n</tr>\n<tr>\n<td><a href=\"https://www.rode.com/accessories/stands/psa1\" class=\"link link-external\">RØDE PSA1 (studio arm)</a></td>\n<td>70€</td>\n</tr>\n<tr>\n<td><a href=\"https://www.rode.com/accessories/psm1\" class=\"link link-external\">RØDE PSM1 (shock mount)</a></td>\n<td>40€</td>\n</tr>\n<tr>\n<td><a href=\"https://www.cordial-cables.com/en/products/cim-fm\" class=\"link link-external\">Cordial Silver Line (2.5m XLR–XLR cable)</a></td>\n<td>21€</td>\n</tr>\n<tr>\n<td><a href=\"https://www.rode.com/accessories/ws2\" class=\"link link-external\">RØDE WS2 (pop filter / wind shield)</a></td>\n<td>16€</td>\n</tr>\n</tbody>\n</table>\n<p>Total price: 436€ 🙈</p>\n<p>In comparison,\nthe Jabra Evolve 65 headset costs about 130€.</p>\n<p>I got the RØDE PSA1 studio arm on sale (70€);\nnormally it costs ~90€.</p>\n<h2 id=\"different-kinds-of-microphones\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#different-kinds-of-microphones\">Different kinds of microphones</a></h2>\n<p>When deciding what microphone to buy,\nI watched some videos.</p>\n<h3 id=\"xlr-vs-usb\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#xlr-vs-usb\">XLR vs USB</a></h3>\n<p>From\n<a href=\"https://www.youtube.com/watch?v=lQOU2oQlH00\" class=\"link link-external\">Podcastage's video <em>XLR vs USB Microphones, Which Should You Buy?</em></a>,\nI learned that:</p>\n<ul>\n<li>USB microphones are easier to use.</li>\n<li>XLR microphones offer better audio quality and more choices,\nbut they require an USB audio interface.</li>\n</ul>\n<p>Thus, I opted for an XLR microphone.\nThere's no such thing as overkill\nwhen it comes to audio stuff,\nright?</p>\n<h3 id=\"dynamic-vs-condenser\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#dynamic-vs-condenser\">Dynamic vs condenser</a></h3>\n<p>From\n<a href=\"https://www.youtube.com/watch?v=Y01N_L1VA4I\" class=\"link link-external\">Podcastage's video <em>Dynamic vs Condenser Microphones, What's the Difference?</em></a>,\nI learned that:</p>\n<ul>\n<li>Condenser microphones are more sensitive\nso they pick up more background noises,\nlike the sound of the keyboard, air conditioner etc.</li>\n<li>Dynamic microphones are better at suppressing background noises\nat the cost of reduced audio quality.</li>\n</ul>\n<p>In my case,\nI'm sure no one (including me) would notice the difference in audio quality\nbetween a dynamic and a condenser microphone.\nBut background noises are always annoying,\nso I opted for a dynamic microphone.</p>\n","summary":"Much better than a Jabra headset,\nbut also much pricier.\nWhy a dynamic XLR microphone\ninstead of a condenser USB microphone?\nRead on to find out.","date_published":"2021-01-15T12:00:00+03:00","date_modified":"2021-08-22T12:00:00+03:00","tags":["Battlestation"]},{"id":"https://mtsknn.fi/blog/locality-of-behavior-and-co-location/","url":"https://mtsknn.fi/blog/locality-of-behavior-and-co-location/","title":"Locality of Behavior / Co-location","content_html":"<p>These two similar principles state that\nthe behavior of code should be obvious on inspection\nand that code should be located where it's relevant.</p>\n<h2 id=\"locality-of-behavior\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#locality-of-behavior\">Locality of Behavior</a></h2>\n<p>The <a href=\"https://htmx.org/essays/locality-of-behaviour/\" class=\"link link-external\"><em>Locality of Behavior</em> principle</a>\nstates that the behavior of code should be obvious on inspection.\nOr more formally:</p>\n<blockquote>\n<p>The behavior of a unit of code\nshould be as obvious as possible\nby looking only at that unit of code.</p>\n</blockquote>\n<p>An example from the linked article:</p>\n<blockquote>\n<p>Consider two different implementations of an AJAX request in HTML,\nthe first in <a href=\"https://htmx.org/\" class=\"link link-external\">htmx</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">hx-get</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/clicked<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>Click Me<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<p>and the second in jQuery:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">$</span><span class=\"token punctuation\">(</span><span class=\"token string\">'#d1'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">on</span><span class=\"token punctuation\">(</span><span class=\"token string\">'click'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">function</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  $<span class=\"token punctuation\">.</span><span class=\"token function\">ajax</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">/* AJAX options... */</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>d1<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>Click Me<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<p>In the former,\nthe behavior of the <code>button</code> element is obvious on inspection,\nsatisfying the Locality of Behavior principle.</p>\n<p>In the latter,\nthe behavior of the <code>button</code> element is spread out amongst multiple files.\nIt is difficult to know exactly what the button does\nwithout a total knowledge of the code base.\nThis &quot;spooky action at a distance&quot; is a source of maintenance issues\nand stands in the way of developers understanding of the code base.</p>\n<p>The htmx example demonstrates good Locality of Behavior,\nwhile the jQuery example has poor Locality of Behavior.</p>\n</blockquote>\n<p><a href=\"https://htmx.org/essays/locality-of-behaviour/\" class=\"link link-external\">Read more about the Locality of Behavior principle</a>\nfrom htmx's docs.</p>\n<h2 id=\"co-location\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#co-location\">Co-location</a></h2>\n<p>The <a href=\"https://kentcdodds.com/blog/colocation\" class=\"link link-external\"><em>Co-location</em> principle</a>\nstates that code should be located where it's relevant.\nOr more formally:</p>\n<blockquote>\n<p>Place code as close to where it's relevant as possible.</p>\n</blockquote>\n<p>And in other words:</p>\n<blockquote>\n<p>Things that change together should be located as close as reasonable.</p>\n</blockquote>\n<p>Some examples from the linked article:</p>\n<ul>\n<li>Code comments are usually &quot;co-located&quot; with the code they are explaining\ninstead of being somewhere else entirely,\ne.g. in a separate <code>docs/</code> folder.\n(This is a contrived example,\nbut demonstrates the importance of the Co-location principle well.)</li>\n<li>React/Vue/etc. components often contain\nboth the view logic and the view template.</li>\n<li>If unit test files are located next to the source files,\nit's very easy to locate the unit tests for a source file.\nFor example,\n<code>Button.js</code> and <code>Button.test.js</code> in the same folder are co-located,\nbut <code>src/.../Button.js</code> and <code>tests/.../Button.test.js</code> are not.</li>\n</ul>\n<p><a href=\"https://kentcdodds.com/blog/colocation\" class=\"link link-external\">Read more about the Co-location principle</a>\nfrom Kent C. Dodds's blog.</p>\n<h2 id=\"example-tailwind\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-tailwind\">Example: Tailwind 🥰</a></h2>\n<p>The Locality of Behavior and Co-location principles partly explain\nwhy I feel productive and happy when using <a href=\"https://tailwindcss.com/\" class=\"link link-external\">Tailwind</a>.</p>\n<p>For example,\nwhen using Tailwind with React,\na single component file can contain everything relevant about the component<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup>:</p>\n<ul>\n<li>HTML structure (JSX)</li>\n<li>event listeners (<code>onClick</code> etc.)</li>\n<li>logic and state (hooks etc.)</li>\n<li>styles (Tailwind classes amongst the JSX).</li>\n</ul>\n<p>With Tailwind,\nI can stay in the component's JS file.\nWithout Tailwind,\nI would need to jump between the component's JS and CSS files.</p>\n<p>To be fair,\nCSS-in-JS could offer similar benefits as Tailwind.</p>\n<p>CSS modules would be worse (separate CSS file),\nbut at least they would be imported from components' JS files,\nso it would be easy to locate a component's styles.</p>\n<p>Global CSS files would be the worst option\nfrom the viewpoint of the Locality of Behavior and Co-location principles.</p>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>I have heard that Vue provides a similarly pleasant experience\nwith its single-file components. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"These two similar principles state that\nthe behavior of code should be obvious on inspection\nand that code should be located where it's relevant.","date_published":"2021-01-15T12:00:00+03:00","date_modified":"2021-10-23T12:00:00+03:00","tags":["Clean code","CSS","JavaScript"]},{"id":"https://mtsknn.fi/blog/calculating-week-numbers-in-js-is-surprisingly-complex/","url":"https://mtsknn.fi/blog/calculating-week-numbers-in-js-is-surprisingly-complex/","title":"Calculating week numbers in JS is surprisingly complex","content_html":"<p>JavaScript doesn't provide a native way\nto get the week number of a date.\nJust use a library instead.</p>\n<p>It might be complex because\n<a href=\"https://en.wikipedia.org/wiki/Week#Week_numbering\" class=\"link link-external\">week numbering itself is complicated</a>.</p>\n<p>In my case,\nI ended up using the\n<a href=\"https://date-fns.org/v2.23.0/docs/getWeek\" class=\"link link-external\"><code>getWeek()</code> function from date-fns</a>.\nThis piece from\n<a href=\"https://github.com/date-fns/date-fns/blob/v2.23.0/src/getWeek/index.ts\" class=\"link link-external\">its implementation on GitHub</a>\nis amusing:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Round the number of days to the nearest integer</span>\n<span class=\"token comment\">// because the number of milliseconds in a week is not constant</span>\n<span class=\"token comment\">// (e.g. it's different in the week of the daylight saving time clock shift)</span>\n<span class=\"token keyword\">return</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">round</span><span class=\"token punctuation\">(</span>diff <span class=\"token operator\">/</span> <span class=\"token constant\">MILLISECONDS_IN_WEEK</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> <span class=\"token number\">1</span>\n</span></code></pre>\n<p>I'm glad I haven't had to deal with date stuff much in the past.</p>\n","summary":"JavaScript doesn't provide a native way\nto get the week number of a date.\nJust use a library instead.","date_published":"2021-01-15T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/how-to-do-overlapping-matches-with-regular-expressions/","url":"https://mtsknn.fi/blog/how-to-do-overlapping-matches-with-regular-expressions/","title":"How to do overlapping matches with regular expressions","content_html":"<p>E.g. how to capture overlapping pairs of letters from the string <code>'abcde'</code>,\ni.e. <code>'ab'</code>, <code>'bc'</code>, <code>'cd'</code> and <code>'de'</code>.\nSpoiler: with lookahead and lookbehind assertions.</p>\n<h2 id=\"consuming-characters-vs-looking-ahead-behind\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#consuming-characters-vs-looking-ahead-behind\">Consuming characters vs looking ahead/behind</a></h2>\n<p>We could try matching two letters at a time, like this:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token string\">'abcde'</span><span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">[a-z][a-z]</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['ab', 'cd']</span>\n</span></code></pre>\n<p>But it doesn't work.\nWhy not?</p>\n<p>Turns out that when the regular expression matches the first two letters\n(<code>a</code> and <code>b</code>),\nit <em>consumes</em> them,\nmeaning that\nthe next match will be done against the rest of the input string,\ni.e. <code>'cde'</code>.\nThat's why the above regular expression doesn't capture overlapping pairs of letters.</p>\n<p>This is what <strong>lookahead and lookbehind assertions</strong> are for:\nthey <em>look ahead or behind to assert (check)</em>\nthat the input contains some characters,\nbut they don't <em>consume</em> those characters.</p>\n<p>We have two options:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> lookahead <span class=\"token operator\">=</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?=[a-z][a-z])</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span>\n<span class=\"token keyword\">const</span> lookbehind <span class=\"token operator\">=</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=[a-z][a-z])</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span>\n</span></code></pre>\n<p>The first regular expression above has a lookahead assertion.\nWhen it's used to loop through the input string,\nit <em>asserts</em> that the current position is followed by two letters.\nIt doesn't <em>consume</em> them because they are inside the lookahead assertion.\nIn fact,\nthe regular expression doesn't consume <em>any</em> characters\nbecause all matchable characters are inside the lookahead assertion.</p>\n<p>The second regular expression above has a lookbehind assertion.\nIt works similarly,\nbut it asserts that the current character is <em>preceded</em> by two letters.</p>\n<p>Here's a table of the lookahead/lookbehind assertion results\nusing the two regular expressions above for the string <code>'abcde'</code>:</p>\n<table>\n<thead>\n<tr>\n<th>Index</th>\n<th>Position <span class=\"whitespace-no-wrap\">(<code>|</code>)</span></th>\n<th>Match</th>\n<th>Look­ahead</th>\n<th>Look­behind</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td><code>|abcde</code></td>\n<td>nothing</td>\n<td><code>ab</code></td>\n<td>nothing</td>\n</tr>\n<tr>\n<td>1</td>\n<td><code>a|bcde</code></td>\n<td>nothing</td>\n<td><code>bc</code></td>\n<td>nothing</td>\n</tr>\n<tr>\n<td>2</td>\n<td><code>ab|cde</code></td>\n<td>nothing</td>\n<td><code>cd</code></td>\n<td><code>ab</code></td>\n</tr>\n<tr>\n<td>3</td>\n<td><code>abc|de</code></td>\n<td>nothing</td>\n<td><code>de</code></td>\n<td><code>bc</code></td>\n</tr>\n<tr>\n<td>4</td>\n<td><code>abcd|e</code></td>\n<td>nothing</td>\n<td>nothing</td>\n<td><code>cd</code></td>\n</tr>\n<tr>\n<td>5</td>\n<td><code>abcde|</code></td>\n<td>nothing</td>\n<td>nothing</td>\n<td><code>de</code></td>\n</tr>\n</tbody>\n</table>\n<p>How does this help us?\nIt does if we use capturing groups.</p>\n<h2 id=\"capturing-groups-inside-lookahead-lookbehind-assertions\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#capturing-groups-inside-lookahead-lookbehind-assertions\">Capturing groups inside lookahead/lookbehind assertions</a></h2>\n<p>We now know how to loop through the input string\nand look ahead/behind to find all (overlapping) pairs of letters.\nWe can <em>capture</em> the pairs\nby using capturing groups inside the lookahead/lookbehind assertions:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Before</span>\n<span class=\"token keyword\">const</span> lookahead <span class=\"token operator\">=</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?=[a-z][a-z])</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span>\n<span class=\"token keyword\">const</span> lookbehind <span class=\"token operator\">=</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=[a-z][a-z])</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span>\n\n<span class=\"token comment\">// After</span>\n<span class=\"token keyword\">const</span> lookahead <span class=\"token operator\">=</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?=([a-z][a-z]))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span>\n<span class=\"token comment\">//                    ^          ^</span>\n<span class=\"token keyword\">const</span> lookbehind <span class=\"token operator\">=</span> <span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=([a-z][a-z]))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span>\n<span class=\"token comment\">//                      ^          ^</span>\n</span></code></pre>\n<p>One caveat, though:\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match\" class=\"link link-external\"><code>String.prototype.match()</code></a>\nignores capturing groups when using the global flag (<code>g</code>).\nSo these don't work:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token string\">'abcde'</span><span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?=([a-z][a-z]))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['', '', '', '']</span>\n\n<span class=\"token string\">'abcde'</span><span class=\"token punctuation\">.</span><span class=\"token function\">match</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=([a-z][a-z]))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['', '', '', '']</span>\n</span></code></pre>\n<p>They don't work because:</p>\n<ul>\n<li>all matchable characters are inside the lookahead/lookbehind assertions,\nso the regular expressions capture nothing</li>\n<li><code>match()</code> ignores capturing groups when using the global flag (<code>g</code>).</li>\n</ul>\n<p>Luckily for us,\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll\" class=\"link link-external\"><code>String.prototype.matchAll()</code></a>\ndoes not ignore capturing groups:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token string\">'abcde'</span><span class=\"token punctuation\">.</span><span class=\"token function\">matchAll</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?=([a-z][a-z]))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> RegExpStringIterator {}</span>\n\n<span class=\"token string\">'abcde'</span><span class=\"token punctuation\">.</span><span class=\"token function\">matchAll</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=([a-z][a-z]))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> RegExpStringIterator {}</span>\n</span></code></pre>\n<p>But oh, <code>matchAll()</code> returns an iterator.\nLet's convert the iterators to arrays:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">'abcde'</span><span class=\"token punctuation\">.</span><span class=\"token function\">matchAll</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?=([a-z][a-z]))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [</span>\n<span class=\"token comment\">//     [\"\", \"ab\", index: 0, input: \"abcde\", groups: undefined],</span>\n<span class=\"token comment\">//     [\"\", \"bc\", index: 1, input: \"abcde\", groups: undefined],</span>\n<span class=\"token comment\">//     [\"\", \"cd\", index: 2, input: \"abcde\", groups: undefined],</span>\n<span class=\"token comment\">//     [\"\", \"de\", index: 3, input: \"abcde\", groups: undefined],</span>\n<span class=\"token comment\">//   ]</span>\n\nArray<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">'abcde'</span><span class=\"token punctuation\">.</span><span class=\"token function\">matchAll</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=([a-z][a-z]))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> [</span>\n<span class=\"token comment\">//     [\"\", \"ab\", index: 2, input: \"abcde\", groups: undefined],</span>\n<span class=\"token comment\">//     [\"\", \"bc\", index: 3, input: \"abcde\", groups: undefined],</span>\n<span class=\"token comment\">//     [\"\", \"cd\", index: 4, input: \"abcde\", groups: undefined],</span>\n<span class=\"token comment\">//     [\"\", \"de\", index: 5, input: \"abcde\", groups: undefined],</span>\n<span class=\"token comment\">//   ]</span>\n</span></code></pre>\n<p>We can see that each array item is an array with\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match#Additional_properties\" class=\"link link-external\">additional properties</a>.\nThe first items are the matches –\nthey are empty strings\nbecause all matchable characters are inside the lookahead/lookbehind assertions.\nThe second items are the matches from the capturing groups.</p>\n<p>Because we are using\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from\" class=\"link link-external\"><code>Array.from()</code></a>,\nwe can easily get only the second array items:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">Array<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">'abcde'</span><span class=\"token punctuation\">.</span><span class=\"token function\">matchAll</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?=([a-z][a-z]))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">match</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> match<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['ab', 'bc', 'cd', 'de']</span>\n\nArray<span class=\"token punctuation\">.</span><span class=\"token function\">from</span><span class=\"token punctuation\">(</span><span class=\"token string\">'abcde'</span><span class=\"token punctuation\">.</span><span class=\"token function\">matchAll</span><span class=\"token punctuation\">(</span><span class=\"token regex\"><span class=\"token regex-delimiter\">/</span><span class=\"token regex-source language-regex\">(?&lt;=([a-z][a-z]))</span><span class=\"token regex-delimiter\">/</span><span class=\"token regex-flags\">g</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">match</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> match<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">//=> ['ab', 'bc', 'cd', 'de']</span>\n</span></code></pre>\n<p>And that's how we can do overlapping matches with regular expressions.\nOr that's the gist of it;\ndepending on your needs,\nyou can also:</p>\n<ul>\n<li>use negative lookahead/lookbehind assertions</li>\n<li>put stuff outside the lookahead/lookbehind assertion.</li>\n</ul>\n<h2 id=\"practical-applications\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#practical-applications\">Practical applications</a></h2>\n<p>Using overlapping regex matches is one way to\n<a href=\"https://mtsknn.fi/blog/converting-a-path-into-cumulative-segments-in-javascript/\" class=\"link\">convert a directory path or URL pathname into cumulative segments</a>;\ne.g. converting\n<code>'/foo/bar/baz/'</code>\ninto an array of\n<code>'/'</code>, <code>'/foo/'</code>, <code>'/foo/bar/'</code> and <code>'/foo/bar/baz/'</code>.</p>\n<p>Anthony Labarre emailed me about another interesting use case:</p>\n<blockquote>\n<p>I used this as an exercise in bash scripting for Chinese students, where I had them extract Chinese &quot;proverbs&quot; from books.\nA faster solution than searching the book for each proverb in a list consists in generating all possibly overlapping subsequences of 4, 5, … characters from the book, and then using <code>comm</code> to identify which subsequences match known proverbs from our list.\nOn my machine, this brought the running time from about 15 seconds to about 1 second.</p>\n<p>For completeness, <a href=\"https://unix.stackexchange.com/a/725518/\" class=\"link link-external\">here's how I did it in bash</a> [...].\n(Except <code>\\w</code> does not work for Chinese characters, you need to substitute it with <code>\\p{Han}</code> and add the <code>-u</code> flag to <code>pcregrep</code>.)</p>\n</blockquote>\n<p>Have you found overlapping regex matches useful in other scenarios?\nLet me know! (Email on the front page.)</p>\n<h2 id=\"browser-support\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#browser-support\">Browser support</a></h2>\n<p>As of March 2022:</p>\n<ul>\n<li><a href=\"https://caniuse.com/sr_js-regexp-lookahead\" class=\"link link-external\">Lookahead assertions are supported by all browsers</a>\nbecause they &quot;are part of JavaScript's original regular expression support.&quot;</li>\n<li><a href=\"https://caniuse.com/js-regexp-lookbehind\" class=\"link link-external\">Lookbehind assertions are supported by all modern browsers except Safari and iOS Safari.</a></li>\n<li><a href=\"https://caniuse.com/mdn-javascript_builtins_string_matchall\" class=\"link link-external\"><code>String.prototype.matchAll()</code> is supported by all modern browsers.</a></li>\n</ul>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<p><a href=\"https://stackoverflow.com/a/33903830/1079869\" class=\"link link-external\">Wiktor Stribiżew's answer to <em>How can I match overlapping strings with regex?</em> on Stack Overflow</a>\nshows how to do overlapping matches by using\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec\" class=\"link link-external\"><code>RegExp.prototype.exec()</code></a>\nand modifying the\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex\" class=\"link link-external\">regular expression instance's <code>lastIndex</code> property</a>.\nThis is useful if you can't use <code>matchAll()</code>,\nwhich isn't supported by Internet Explorer.</p>\n<p>Wiktor's answer also shows the solution that I have presented here;\nI actually learned it from Wiktor's answer (thanks!).\nThis blog post is mainly me teaching myself the topic\nby teaching it to you.</p>\n","summary":"E.g. how to capture overlapping pairs of letters from the string `abcde`,\ni.e. `ab`, `bc`, `cd` and `de`.\nSpoiler: with lookaheads and lookbehinds.","date_published":"2021-01-13T12:00:00+03:00","date_modified":"2022-12-07T12:00:00+03:00","tags":["JavaScript","Regular expressions"]},{"id":"https://mtsknn.fi/blog/fn-apply-vs-fn-call-in-javascript-better-alternative/","url":"https://mtsknn.fi/blog/fn-apply-vs-fn-call-in-javascript-better-alternative/","title":"`fn.apply()` vs `fn.call()` in JavaScript + better alternative","content_html":"<p>Mnemonic:\n&quot;a&quot; (<code>apply()</code>) for an array,\n&quot;c&quot; (<code>call()</code>) for commas.\nBut <code>Reflect.apply()</code> is better.</p>\n<h2 id=\"fnapply-vs-fncall\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#fnapply-vs-fncall\"><code>fn.apply()</code> vs <code>fn.call()</code></a></h2>\n<p>The\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply\" class=\"link link-external\"><code>apply()</code> method</a>\ntakes arguments as an array,\nand the\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call\" class=\"link link-external\"><code>call()</code> method</a>\ntakes multiple arguments:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">foo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> thisArg <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> args <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">]</span>\n\n<span class=\"token function\">foo</span><span class=\"token punctuation\">.</span><span class=\"token function\">apply</span><span class=\"token punctuation\">(</span>thisArg<span class=\"token punctuation\">,</span> args<span class=\"token punctuation\">)</span>\n<span class=\"token function\">foo</span><span class=\"token punctuation\">.</span><span class=\"token function\">apply</span><span class=\"token punctuation\">(</span>thisArg<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token function\">foo</span><span class=\"token punctuation\">.</span><span class=\"token function\">call</span><span class=\"token punctuation\">(</span>thisArg<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>args<span class=\"token punctuation\">)</span>\n<span class=\"token function\">foo</span><span class=\"token punctuation\">.</span><span class=\"token function\">call</span><span class=\"token punctuation\">(</span>thisArg<span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Mnemonic:</p>\n<ul>\n<li><strong>a</strong> (<code>apply()</code>) for an <strong>a</strong>rray</li>\n<li><strong>c</strong> (<code>call()</code>) for <strong>c</strong>ommas.</li>\n</ul>\n<p>Source for the mnemonic:\n<a href=\"https://stackoverflow.com/q/1986896/1079869\" class=\"link link-external\"><em>What is the difference between call and apply?</em> on Stack Overflow</a>.</p>\n<h2 id=\"the-better-alternative\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-better-alternative\">The better alternative</a></h2>\n<p><a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/apply\" class=\"link link-external\"><code>Reflect.apply()</code></a>\nis better.\nLike <code>fn.apply()</code>,\nit takes arguments as an array:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">foo</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> thisArg <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> args <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">]</span>\n\n<span class=\"token function\">Reflect</span><span class=\"token punctuation\">.</span><span class=\"token function\">apply</span><span class=\"token punctuation\">(</span>foo<span class=\"token punctuation\">,</span> thisArg<span class=\"token punctuation\">,</span> args<span class=\"token punctuation\">)</span>\n<span class=\"token function\">Reflect</span><span class=\"token punctuation\">.</span><span class=\"token function\">apply</span><span class=\"token punctuation\">(</span>foo<span class=\"token punctuation\">,</span> thisArg<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>It's &quot;arguably less verbose and easier to understand&quot; than <code>fn.apply()</code>.\n&quot;In addition,\nwhen you accept arbitrary methods,\nit's not safe to assume <code>.apply()</code> exists or is not overridden.&quot;</p>\n<p>Source:\n<a href=\"https://github.com/sindresorhus/eslint-plugin-unicorn/blob/v25.0.1/docs/rules/prefer-reflect-apply.md\" class=\"link link-external\"><em>Prefer <code>Reflect.apply()</code> over <code>Function#apply()</code></em> ESLint rule</a>\nfrom\n<a href=\"https://github.com/sindresorhus/eslint-plugin-unicorn\" class=\"link link-external\">eslint-plugin-unicorn</a>.</p>\n<h3 id=\"a-note-about-verbosity\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#a-note-about-verbosity\">A note about verbosity</a></h3>\n<p>This is not verbose:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">foo</span><span class=\"token punctuation\">.</span><span class=\"token function\">apply</span><span class=\"token punctuation\">(</span>thisArg<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>But it's not safe either as stated above.</p>\n<p>This is safe but also verbose:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token class-name\">Function</span><span class=\"token punctuation\">.</span>prototype<span class=\"token punctuation\">.</span><span class=\"token function\">apply</span><span class=\"token punctuation\">.</span><span class=\"token function\">call</span><span class=\"token punctuation\">(</span>foo<span class=\"token punctuation\">,</span> thisArg<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<p>Now it's easy to see\nwhy <code>Reflect.apply()</code> is &quot;arguably less verbose and easier to understand&quot;:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">Reflect</span><span class=\"token punctuation\">.</span><span class=\"token function\">apply</span><span class=\"token punctuation\">(</span>foo<span class=\"token punctuation\">,</span> thisArg<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n</span></code></pre>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<ul>\n<li><a href=\"https://flems.io/#0=N4IgtglgJlA2CmIBcBGADGgNCAZhBAzsgNqgB2AhmIkiAHQBWR2AxgPZkAu8XyIA7hDJQ2-OjjZsABAF4pAHQFCR-AOQEpEtovllBw0XQBGFAF6yFSg2o0nTOsrpwBXMi04QOmsgAo6-igAnAHMCAEopYF0pKSDQ8TZAgFEKFgALHx84iJkAPil2MgI2BDpYNmCskMwpVSRVGs40iAJiOIBdMLDdAF9dXUKCTik2IwYLKLIYrSRLUYZ1TUlFTGipE0DZxXnFjZXegY4h2JCNOWJVLQbajevVO1V2-rcjkvgyip9VKAhA+HcChRYLBVN0yDhfP46HFws9Bm8PpVVPInGRoQAHdGwACePjCoNRGKxuPmNRhYMORQR5SRKPBaJYQNgeIJ9LojOBPlJUih5Lhr1KNK+dIASvAcAh3EScSywWKJf9ONLcRCaty+WQQNgCPBJR4jnwAEwAZiQhoAHCAeu0ekA\" class=\"link link-external\">Test the code on flems.io</a></li>\n<li><a href=\"https://old.reddit.com/r/javascript/comments/klkj1e/60_useful_eslint_rules/\" class=\"link link-external\">Recent (December 2020) Reddit discussion about eslint-plugin-unicorn</a></li>\n</ul>\n","summary":"Mnemonic:\n\"a\" (`apply()`) for an array,\n\"c\" (`call()`) for commas.\nBut `Reflect.apply()` is better.","date_published":"2021-01-11T12:00:00+03:00","date_modified":"2021-10-26T12:00:00+03:00","tags":["JavaScript","Mnemonics"]},{"id":"https://mtsknn.fi/blog/when-to-use-the-xmlns-and-version-svg-attributes/","url":"https://mtsknn.fi/blog/when-to-use-the-xmlns-and-version-svg-attributes/","title":"When to use the `xmlns` and `version` SVG attributes","content_html":"<p>The <code>xmlns</code> attribute is required for SVG files\nbut optional for inlined SVGs.\nThe <code>version</code> attribute is always optional.</p>\n<h2 id=\"the-xmlns-and-xmlnsxlink-attributes\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-xmlns-and-xmlnsxlink-attributes\">The <code>xmlns</code> and <code>xmlns:xlink</code> attributes</a></h2>\n<ul>\n<li>\n<p>The <code>xmlns</code> and <code>xmlns:xlink</code> attributes are\n<strong>optional for inlined SVGs in HTML</strong>.</p>\n</li>\n<li>\n<p>The <code>xmlns</code> attribute is\n<strong>required for SVG files</strong>.</p>\n</li>\n<li>\n<p>The <code>xmlns</code> attribute is\n<strong>required for SVGs in CSS</strong>, e.g.:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-css bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token property\">background-image</span><span class=\"token punctuation\">:</span> <span class=\"token function\">url</span><span class=\"token punctuation\">(</span>\n  <span class=\"token string\">'data:image/svg+xml;utf8,&lt;svg xmlns=\"http://www.w3.org/2000/svg\">...&lt;/svg>'</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n</li>\n<li>\n<p>The <code>xmlns:xlink</code> attribute is required\nfor SVG files that contain <code>xlink:</code> attributes.\nSimilarly for SVGs in CSS.</p>\n</li>\n</ul>\n<h2 id=\"the-version-attribute\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-version-attribute\">The <code>version</code> attribute</a></h2>\n<p>The <code>version</code> attribute is ignored and thus <strong>always optional</strong>.\nPlus it's deprecated since SVG 2.</p>\n<h2 id=\"source\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#source\">Source</a></h2>\n<p><a href=\"https://stackoverflow.com/q/18467982/1079869\" class=\"link link-external\"><em>Are SVG parameters such as 'xmlns' and 'version' needed?</em> on Stack Overflow</a></p>\n","summary":"The `xmlns` attribute is required for SVG files\nbut optional for inlined SVGs.\nThe `version` attribute is always optional.","date_published":"2021-01-08T12:00:00+03:00","date_modified":"2021-06-12T12:00:00+03:00","tags":["SVG"]},{"id":"https://mtsknn.fi/blog/uncontrolled-form-inputs-in-react/","url":"https://mtsknn.fi/blog/uncontrolled-form-inputs-in-react/","title":"Uncontrolled form inputs in React","content_html":"<p>If you need form input values only at the time of form submission,\nuncontrolled inputs can be nice to work with.</p>\n<p>From <a href=\"https://reactjs.org/docs/uncontrolled-components.html\" class=\"link link-external\"><em>Uncontrolled Components</em> on React docs</a>:</p>\n<blockquote>\n<p>In most cases,\nwe recommend using <a href=\"https://reactjs.org/docs/forms.html#controlled-components\" class=\"link link-external\">controlled components</a>\nto implement forms.\nIn a controlled component,\nform data is handled by a React component.\nThe alternative is uncontrolled components,\nwhere form data is handled by the DOM itself.</p>\n</blockquote>\n<p>I previously thought that\nusing uncontrolled inputs in React\nis very rare or even a bad practice.</p>\n<p>But then I found this\n<a href=\"https://twitter.com/JoshWComeau/status/1330302183431147526\" class=\"link link-external\">tweet by Josh W. Comeau</a>:</p>\n<blockquote>\n<p>🌠 I've been really enjoying using uncontrolled form inputs recently.\nSet an initial value with React,\nand then collect the values using standard JS when the form is submitted.</p>\n<p>No fussing with state required 🔥</p>\n<p><a href=\"https://codesandbox.io/s/jovial-prosk\" class=\"link link-external\">CodeSandbox demo</a></p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">EditUser</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> user <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">function</span> <span class=\"token function\">saveUser</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">ev</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    ev<span class=\"token punctuation\">.</span><span class=\"token function\">preventDefault</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token keyword\">const</span> elementsArray <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token operator\">...</span>ev<span class=\"token punctuation\">.</span>target<span class=\"token punctuation\">.</span>elements<span class=\"token punctuation\">]</span>\n    <span class=\"token keyword\">const</span> formData <span class=\"token operator\">=</span> elementsArray<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">acc<span class=\"token punctuation\">,</span> elem</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>elem<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span> acc<span class=\"token punctuation\">[</span>elem<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> elem<span class=\"token punctuation\">.</span>value\n      <span class=\"token keyword\">return</span> acc\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n    <span class=\"token comment\">// Do a `fetch` or whatever with `formData`!</span>\n    <span class=\"token comment\">// Has the shape: `{ name: string, email: string }`</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>form</span> <span class=\"token attr-name\">onSubmit</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>saveUser<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>label</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n        Name\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>text<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>name<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">defaultValue</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>user<span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">}</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>label</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">Save</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>form</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n</blockquote>\n<p>It makes sense to use uncontrolled form inputs\nif the input values are needed only at the time of form submission.\nI'd imagine the performance must be better as well.</p>\n<p>From the replies on Twitter:\ncreating a <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/FormData\" class=\"link link-external\"><code>FormData</code> object</a>\nsimplifies the <code>saveUser</code> function even more:</p>\n<pre aria-label=\"Code block\" class=\"language-diff-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span><span class=\"token keyword\">function</span> <span class=\"token function\">saveUser</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">ev</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix unchanged\"> </span>  ev<span class=\"token punctuation\">.</span><span class=\"token function\">preventDefault</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n</span>\n<span class=\"token deleted-sign deleted language-js\"><span class=\"token prefix deleted\">-</span>  <span class=\"token keyword\">const</span> elementsArray <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token operator\">...</span>ev<span class=\"token punctuation\">.</span>target<span class=\"token punctuation\">.</span>elements<span class=\"token punctuation\">]</span>\n<span class=\"token prefix deleted\">-</span>  <span class=\"token keyword\">const</span> formData <span class=\"token operator\">=</span> elementsArray<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">acc<span class=\"token punctuation\">,</span> elem</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n<span class=\"token prefix deleted\">-</span>    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>elem<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">)</span> acc<span class=\"token punctuation\">[</span>elem<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> elem<span class=\"token punctuation\">.</span>value\n<span class=\"token prefix deleted\">-</span>    <span class=\"token keyword\">return</span> acc\n<span class=\"token prefix deleted\">-</span>  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n</span><span class=\"token inserted-sign inserted language-js\"><span class=\"token prefix inserted\">+</span>  <span class=\"token keyword\">const</span> formData <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FormData</span><span class=\"token punctuation\">(</span>ev<span class=\"token punctuation\">.</span>target<span class=\"token punctuation\">)</span>\n</span>\n<span class=\"token unchanged language-js\"><span class=\"token prefix unchanged\"> </span>  <span class=\"token comment\">// Do a `fetch` or whatever with `formData`!</span>\n<span class=\"token prefix unchanged\"> </span><span class=\"token punctuation\">}</span>\n</span></span></code></pre>\n<p>Note that form inputs need to have a <code>name</code> prop/attribute\nor they won't end up in the <code>formData</code> object (if using <code>new FormData(ev.target)</code>).</p>\n<p>Usage of the <code>formData</code> object:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">saveUser</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">ev</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  ev<span class=\"token punctuation\">.</span><span class=\"token function\">preventDefault</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">const</span> formData <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">FormData</span><span class=\"token punctuation\">(</span>ev<span class=\"token punctuation\">.</span>target<span class=\"token punctuation\">)</span>\n\n  <span class=\"token comment\">// `Content-Type` will implicitly be `multipart/form-data`</span>\n  <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">body</span><span class=\"token operator\">:</span> formData<span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">method</span><span class=\"token operator\">:</span> <span class=\"token string\">'POST'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token comment\">// or</span>\n\n  <span class=\"token keyword\">const</span> pojo <span class=\"token operator\">=</span> Object<span class=\"token punctuation\">.</span><span class=\"token function\">fromEntries</span><span class=\"token punctuation\">(</span>formData<span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">const</span> json <span class=\"token operator\">=</span> <span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">stringify</span><span class=\"token punctuation\">(</span>pojo<span class=\"token punctuation\">)</span>\n  <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">body</span><span class=\"token operator\">:</span> json<span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">headers</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token string-property property\">'Content-Type'</span><span class=\"token operator\">:</span> <span class=\"token string\">'application/json'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">method</span><span class=\"token operator\">:</span> <span class=\"token string\">'POST'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p><a href=\"https://flems.io/#0=N4IgZglgNgpgziAXAbVAOwIYFsZJAOgAsAXLKEAGhAGMB7NYmBvAHgBMIA3AAgjYF4AOiAwAHUcIB8LAPQdOkkAF8K6bLkQEAVgip0GTYnhkzuJYqLiITWYnADWaNPkgyARlFoBzGQFc0+sQATrRQsGwAtGC0QVgREGiivnbxaBFBMBjUxDKCaHlg-tkQ9NwAohzEAKpwMEEAFMDcvrVB3EoAlNzAedzchQHEJWjccBicMDV19TCcXT0jfdyz+KIZEwwAIjBgGL5QxPUdvUsnffpwxP0xWJsYxBjc-NxoMADu3ABiN3cPM5z4B5BLwwYjHRZ9M7cEzcAAGAGF6IwGBEACoAT1EMFh3De0CgvCwoigEGoEGIUHR3DcMDhWH2Q1EGCCOWisQibHuGFhULAoOohHqAHIZIxLkKKN0oX03LQ2OjENdYr8MBRpdwcMRCHLFUKAAoAeQAyqiJVDOlCoTCYmcoRcrqJaFpaE9uAa3FoYNkXCEsGUGEEIPB6mzblzwUtuPbuDpSs8AFJGg0AOXwl0DaC8EDA6PqjudEaWfOIAuFovgxAlUohS1l8sVsbQaprfUImTYdSs3W4QsRBhRGKxQt1YmJpPuwxkjaF7Wbkb6mu1bF1hpNZprFvyNYwsBZwvhbeo9m4WtpxYF3AyAEdfBW4LwRqfuMnQW8YseHm5uLQwNxtpxUVoUI4CFQslDyE4MmIXwghGeooRYUNvzQI1fDcLByX4YAxgmKYgiUSR1RYKAMBpKBCJbJZk3UYAZyFcDKL6FgEiSK5iExGAhBARgAA9iGEXgBGETAcAEkTOOE9QBI7XYGQANR3W8sJaOp8HEpRoQo+duFkEiyK0+diNImByPVJYyiwDBoFonsGO0pZmMSZITw4riYEs6ABL4NyPKgMT1B8qy-JAbgZL2A4FKgJTgBUoJ8HcoKNJkAzDJkPSTJShy3GSYh6EkI1xhgWRsuIXK0Ey2RQwM8EGLyAAlTJsk2A0AFl8AyNAOwaE4WAqck8OaVosKacTdXjWhCDQGclCSyQ51C2hqF8HAGHwG86nRI0TK9XKGiFABiUdQLycFKBoWgiWgOo8DcYzyBUNQcDwfBqDgXRzv7IxNHSgkFj6Dg4GJDAFWpTwjwAbjyJQztqWBinoBBNAARkQJGAHZlAAXSoEk0HsRHUBAcS8AyLIjCoGDyE0cxLGsPxEnsLwXoumRSeyPwsDYVnGuIfAOwmTxRBW3mdDO9isTwOBqEDUQjAeon1BJnmOQus7KbwGmrBMfxREZ5msG5smVYN5aubZ4hjb52YTNoIXDHwUWqHFjQQClmW5exhWns0KoAiREIwhgNglSwB9WPvBJuAasnlCAA\" class=\"link link-external\">Demo on flems.io</a></p>\n","summary":"If you need form input values only at the time of form submission,\nuncontrolled inputs can be nice to work with.","date_published":"2021-01-08T12:00:00+03:00","date_modified":"2021-10-16T12:00:00+03:00","tags":["JavaScript","React"]},{"id":"https://mtsknn.fi/blog/notes-on-tail-call-optimization-the-musical/","url":"https://mtsknn.fi/blog/notes-on-tail-call-optimization-the-musical/","title":"My notes on \"Tail Call Optimization: The Musical!\"","content_html":"<p>My quick notes on a funny but informative video (11:27)\nthat explains how tail call optimization (TCO) works.\nBonus: does JS support TCO?\nSpoiler: yes but no.</p>\n<h2 id=\"the-video\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-video\">The video</a></h2>\n<p><a href=\"https://www.youtube.com/watch?v=-PX0BV9hGZY\" class=\"link link-external\">Watch the video on YouTube</a></p>\n<p>(Note to self:\nembed the video using <a href=\"https://github.com/paulirish/lite-youtube-embed\" class=\"link link-external\">Lite YouTube Embed</a>.)</p>\n<h2 id=\"my-notes\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#my-notes\">My notes</a></h2>\n<ul>\n<li>Tail call = a function call at the very end of a function,\ni.e. the function call has to be the very last thing that the function does.</li>\n<li>Tail call optimization (TCO) = if the language (and engine) supports TCO,\ntail calls are removed from the call stack\nbecause they are kind of unnecessarily there\n(because of the tail position).\n<ul>\n<li>Pro:\nYou don't get errors saying\n&quot;Maximum call stack size exceeded&quot; (Chrome)\nor &quot;Too much recursion&quot; (Firefox)\nor &quot;Stack overflow&quot; (some other contexts).</li>\n<li>Con:\nStack traces have less information\n(because tail calls have been removed from the call stack),\nmaking debugging errors potentially more difficult.</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"bonus-does-javascript-support-tco\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#bonus-does-javascript-support-tco\">Bonus: Does JavaScript support TCO?</a></h2>\n<p>Not in the video but relevant:\nECMAScript 6 offers TCO,\nbut TCO &quot;isn't supported by many [JavaScript] engines and that may never change.&quot;\nSource:\n<a href=\"https://2ality.com/2015/06/tail-call-optimization.html\" class=\"link link-external\"><em>Tail call optimization in ECMAScript 6</em> by Dr. Axel Rauschmayer</a>.</p>\n<p>The <a href=\"https://kangax.github.io/compat-table/es6/#test-proper_tail_calls_(tail_call_optimisation)\" class=\"link link-external\">ECMAScript 6 compatibility table</a>\nshows that there indeed are not many browsers/engines that support TCO –\nonly these (as of Aug 26, 2021):</p>\n<ul>\n<li>Safari 14+</li>\n<li>iOS Safari 12+</li>\n<li>WebKit, the browser engine used by Safari and iOS Safari</li>\n<li><a href=\"https://github.com/Kinoma/kinomajs\" class=\"link link-external\">Kinoma XS6</a>,\n&quot;a JavaScript runtime optimized for the applications that power IoT devices&quot;</li>\n</ul>\n","summary":"My quick notes on a funny but informative video (11:27)\nthat explains how tail call optimization (TCO) works.\nBonus: does JS support TCO?\nSpoiler: yes but no.","date_published":"2021-01-08T12:00:00+03:00","date_modified":"2021-08-26T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/how-to-sort-package-json-automatically/","url":"https://mtsknn.fi/blog/how-to-sort-package-json-automatically/","title":"How to sort `package.json` automatically","content_html":"<p>Just run <code>npx sort-package-json</code>.</p>\n<p>This takes about 4–8 seconds on my machine\nbecause <code>npx</code> has to first download the <a href=\"https://www.npmjs.com/package/sort-package-json\" class=\"link link-external\"><code>sort-package-json</code> package</a>.\nIf that's too slow for you,\ninstall the package globally:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">#  i =   install</span>\n<span class=\"token comment\"># -g = --global</span>\n<span class=\"token function\">npm</span> i -g sort-package-json\n</span></code></pre>\n<p>Then you can run just:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">sort-package-json\n</span></code></pre>\n<p>And it will take less than a second.</p>\n<p>You can also install the package locally\nand even run it in Git's pre-commit hook (using <a href=\"https://www.npmjs.com/package/husky\" class=\"link link-external\">Husky</a> or <a href=\"https://github.com/Arkweid/lefthook\" class=\"link link-external\">Lefthook</a>)\nor in your CI pipeline.\nMore options are mentioned in <a href=\"https://www.npmjs.com/package/sort-package-json\" class=\"link link-external\"><code>sort-package-json</code>'s documentation</a>.</p>\n<p>Source: <a href=\"https://stackoverflow.com/a/48276232/1079869\" class=\"link link-external\">My own Stack Overflow answer</a></p>\n","summary":"Just run `npx sort-package-json`.","date_published":"2021-01-06T12:00:00+03:00","date_modified":"2021-06-12T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/how-to-force-push-in-git-with-style-and-some-safety/","url":"https://mtsknn.fi/blog/how-to-force-push-in-git-with-style-and-some-safety/","title":"How to force push in Git with style (and some safety)","content_html":"<p>Never run <code>git push --force</code>.\nInstead run <code>git push --force-with-lease</code>\nor alias it to <code>git please</code>.</p>\n<h2 id=\"please-be-polite\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#please-be-polite\">Please be polite</a></h2>\n<p>Never run <code>git push --force</code>\nor you might overwrite commits pushed by others.\nInstead run:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">git</span> push --force-with-lease\n</span></code></pre>\n<p>Or if using\n<a href=\"https://github.com/mtsknn/dotfiles/blob/master/.gitconfig\" class=\"link link-external\">my Git aliases</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">git</span> please\n</span></code></pre>\n<p>Please = force <strong>p</strong>ush with <strong>lease</strong>.\nSo polite!</p>\n<p>(Unfortunately I don't remember where I found this alias,\nso I can't give proper credit.)</p>\n<h2 id=\"force-vs-force-with-lease\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#force-vs-force-with-lease\"><code>--force</code> vs <code>--force-with-lease</code></a></h2>\n<p>Git's manual is too wordy,\nso I'm relying on the answers to\n<a href=\"https://stackoverflow.com/q/52823692/1079869\" class=\"link link-external\"><em><code>git push --force-with-lease</code> vs <code>--force</code></em> on Stack Overflow</a>.</p>\n<p>chevybow writes (slightly modified and shortened):</p>\n<blockquote>\n<p><code>--force</code> overwrites a remote branch with your local branch.</p>\n<p><code>--force-with-lease</code> is a safer option\nthat will not overwrite any work on the remote branch\nif more commits were added to the remote branch\n(by another team member or coworker or what have you).\nIt ensures you don't overwrite someone else's work by force pushing.</p>\n<p>I just think of <code>--force-with-lease</code> as the option to use\nwhen I want to make sure I don't overwrite any teammate's code.\nA lot of teams at my company use <code>--force-with-lease</code> as the default option for a fail-safe.\nIt's unnecessary in most circumstances\nbut will save you lots of headache\nif you happen to overwrite something that another person contributed to remote.</p>\n</blockquote>\n<h3 id=\"warning-fetching-is-risky\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#warning-fetching-is-risky\">Warning: fetching is risky</a></h3>\n<p>G. Sylvie Davies writes:</p>\n<blockquote>\n<p>There's a sad implication here:\nsince <code>git fetch</code> updates all refs under &quot;refs/remotes/origin/*&quot; to their latest versions,\nthis combination of commands is essentially identical to <code>git push --force</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">git</span> fetch\n\n<span class=\"token comment\"># The command below behaves identically to \"git push --force\"</span>\n<span class=\"token comment\"># if a \"git fetch\" just happened!</span>\n\n<span class=\"token function\">git</span> push --force-with-lease\n</span></code></pre>\n<p>To work around this inherent weakness in <code>git push --force-with-lease</code>\nI try to never run <code>git fetch</code>.\nInstead I always run <code>git pull --rebase</code>\nwhenever I need to sync with upstream,\nsince <code>git pull</code> only updates a single ref under refs/remotes,\nkeeping the &quot;lease&quot; of <code>--force-with-lease</code> useful.</p>\n</blockquote>\n<p>Does your code editor or IDE do automatic fetching?\nYou might want to disable that\nunless you can accept the risk.</p>\n<h2 id=\"when-to-force-push-with-lease\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#when-to-force-push-with-lease\">When to force push (with lease)</a></h2>\n<p>Some say that one should never force push.\nI say that's bollocks.</p>\n<p>If you are working on your own short-lived branch (not <code>master</code> or <code>main</code>),\nfeel free to rewrite its history:\nrebase, squash etc.\nAfter rewriting a branch's history,\na force push is needed.</p>\n<p>The &quot;if&quot; in the previous paragraph is a misnomer.\n<em>Of course</em> you are working on short-lived branches\nand not directly on the <code>master</code>/<code>main</code> branch,\nright?\nIf you are not yet doing\n<a href=\"https://trunkbaseddevelopment.com/\" class=\"link link-external\">trunk-based development</a>,\nlook it up.</p>\n","summary":"Never run `git push --force`.\nInstead run `git push --force-with-lease`\nor alias it to `git please`.","date_published":"2021-01-04T12:00:00+03:00","date_modified":"2021-06-11T12:00:00+03:00","tags":["Git"]},{"id":"https://mtsknn.fi/blog/how-to-undo-the-last-previous-most-recent-commit-in-git/","url":"https://mtsknn.fi/blog/how-to-undo-the-last-previous-most-recent-commit-in-git/","title":"How to undo the last / previous / most recent commit in Git","content_html":"<p>Run <code>git reset HEAD~</code> or alias it to <code>git undo</code>.\nBonus: how to use the commit message from the undoed commit.</p>\n<h2 id=\"tldr\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#tldr\">TL;DR</a></h2>\n<p>Undo the last/previous/most recent commit\n<em>while preserving the changes</em>:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\"># Keep the undoed changes</span>\n<span class=\"token function\">git</span> reset HEAD~\n\n<span class=\"token comment\"># or</span>\n\n<span class=\"token comment\"># Keep the undoed changes in the staging area</span>\n<span class=\"token function\">git</span> reset HEAD~ --soft\n</span></code></pre>\n<p>Afterwards,\nto do a commit and use the commit message from the undoed commit:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">git</span> commit -c ORIG_HEAD\n</span></code></pre>\n<p>If using\n<a href=\"https://github.com/mtsknn/dotfiles/blob/master/.gitconfig\" class=\"link link-external\">my Git aliases</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">git</span> undo\n<span class=\"token comment\"># or</span>\n<span class=\"token function\">git</span> undo --soft\n\n<span class=\"token comment\"># Use the commit message from the undoed commit</span>\n<span class=\"token function\">git</span> redo\n</span></code></pre>\n<h2 id=\"explanation\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#explanation\">Explanation</a></h2>\n<h3 id=\"undoing\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#undoing\">Undoing</a></h3>\n<ul>\n<li><code>HEAD</code> is your current &quot;position&quot; in Git's history;\nit points to the commit that you have currently checked out.</li>\n<li><code>HEAD~</code> points to <code>HEAD</code>'s parent commit,\ni.e. to the previous commit of your current position.</li>\n<li><code>git reset HEAD~</code> resets the head to <code>HEAD~</code>,\ni.e. to the previous commit.</li>\n</ul>\n<p>From <a href=\"https://git-scm.com/docs/git-reset\" class=\"link link-external\"><code>git reset</code>'s documentation</a>:</p>\n<blockquote>\n<pre aria-label=\"Code block\" class=\"language-bash bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token function\">git</span> reset <span class=\"token punctuation\">[</span><span class=\"token operator\">&lt;</span>mode<span class=\"token operator\">></span><span class=\"token punctuation\">]</span> <span class=\"token punctuation\">[</span><span class=\"token operator\">&lt;</span>commit<span class=\"token operator\">></span><span class=\"token punctuation\">]</span>\n</span></code></pre>\n<p>This form resets the current branch head to <code>&lt;commit&gt;</code>\nand possibly updates the index (resetting it to the tree of <code>&lt;commit&gt;</code>)\nand the working tree depending on <code>&lt;mode&gt;</code>.\nIf <code>&lt;mode&gt;</code> is omitted,\ndefaults to <code>--mixed</code>.\nThe <code>&lt;mode&gt;</code> must be one of the following:</p>\n<dl>\n<dt><code>--soft</code></dt>\n<dd>Does not touch the index file or the working tree at all\n(but resets the head to <code>&lt;commit&gt;</code>, just like all modes do).\nThis leaves all your changed files &quot;Changes to be committed&quot;,\nas <code>git status</code> would put it.</dd>\n<dt><code>--mixed</code></dt>\n<dd>Resets the index but not the working tree\n(i.e., the changed files are preserved but not marked for commit)\nand reports what has not been updated.\nThis is the default action.</dd>\n</dl>\n</blockquote>\n<p>&quot;The index&quot; refers to the staging area\n(the &quot;Changes to be committed&quot; area).</p>\n<p><code>--mixed</code> is the default mode,\nso <code>git reset HEAD~</code> resets the index,\nmeaning that the undoed changes are not kept in the staging area\n(but the changes are preserved).</p>\n<p><code>git reset HEAD~ --soft</code> on the other hand\ndoesn't touch the index file,\nmeaning that the undoed changes <em>are</em> kept in the staging area.</p>\n<p>Should you use the <code>--soft</code> flag?\nDepends on whether you want the undoed changes to be in the staging area.</p>\n<h3 id=\"redoing\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#redoing\">Redoing</a></h3>\n<p>From <a href=\"https://git-scm.com/docs/git-commit\" class=\"link link-external\"><code>git commit</code>'s documentation</a>:</p>\n<blockquote>\n<dl>\n<dt><code>-C &lt;commit&gt;</code></dt>\n<dt><code>--reuse-message=&lt;commit&gt;</code></dt>\n<dd>\nTake an existing commit object,\nand reuse the log message\nand the authorship information (including the timestamp)\nwhen creating the commit.\n</dd>\n<dt><code>-c &lt;commit&gt;</code></dt>\n<dt><code>--reedit-message=&lt;commit&gt;</code></dt>\n<dd>\nLike <code>-C</code>,\nbut with <code>-c</code> the editor is invoked,\nso that the user can further edit the commit message.\n</dd>\n</dl>\n</blockquote>\n<p>From <a href=\"https://git-scm.com/docs/gitrevisions\" class=\"link link-external\"><code>gitrevisions</code>'s documentation</a>:</p>\n<blockquote>\n<p><code>ORIG_HEAD</code> is created by commands that move your <code>HEAD</code> in a drastic way,\nto record the position of the <code>HEAD</code> before their operation,\nso that you can easily change the tip of the branch\nback to the state before you ran them.</p>\n</blockquote>\n<p>In other words,\n<code>ORIG_HEAD</code> points to the previous <code>HEAD</code> –\nin this case to the undoed commit.</p>\n<p>Thus,\n<code>git commit -c ORIG_HEAD</code>\nuses the commit message from the undoed commit\nand opens the editor so you can edit the message before committing.</p>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<ul>\n<li><a href=\"https://git-scm.com/docs/git-commit\" class=\"link link-external\"><code>git commit</code>'s documentation</a></li>\n<li><a href=\"https://git-scm.com/docs/git-reset\" class=\"link link-external\"><code>git reset</code>'s documentation</a></li>\n<li><a href=\"https://git-scm.com/docs/gitrevisions\" class=\"link link-external\"><code>gitrevisions</code>'s documentation</a></li>\n<li><a href=\"https://stackoverflow.com/q/2304087/1079869\" class=\"link link-external\"><em>What is <code>HEAD</code> in Git?</em> on Stack Overflow</a></li>\n<li><a href=\"https://stackoverflow.com/q/2221658/1079869\" class=\"link link-external\"><em>What's the difference between <code>HEAD^</code> and <code>HEAD~</code> in Git?</em> on Stack Overflow</a></li>\n</ul>\n","summary":"Run `git reset HEAD~` or alias it to `git undo`.\nBonus: how to use the commit message from the undoed commit.","date_published":"2021-01-02T12:00:00+03:00","date_modified":"2021-06-12T12:00:00+03:00","tags":["Git"]},{"id":"https://mtsknn.fi/blog/how-to-chunk-an-array-in-javascript/","url":"https://mtsknn.fi/blog/how-to-chunk-an-array-in-javascript/","title":"How to chunk an array in JavaScript","content_html":"<p>A simple reducer\nto chunk (split) an array into smaller arrays.</p>\n<p>For example, with a chunk size of 3:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token comment\">// Before</span>\n<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token number\">4</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">6</span><span class=\"token punctuation\">,</span> <span class=\"token number\">7</span><span class=\"token punctuation\">,</span> <span class=\"token number\">8</span><span class=\"token punctuation\">,</span> <span class=\"token number\">9</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">]</span>\n\n<span class=\"token comment\">// After</span>\n<span class=\"token punctuation\">[</span><span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token number\">4</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">6</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token number\">7</span><span class=\"token punctuation\">,</span> <span class=\"token number\">8</span><span class=\"token punctuation\">,</span> <span class=\"token number\">9</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token number\">10</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span>\n</span></code></pre>\n<p>Here's how:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">chunk</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">array<span class=\"token punctuation\">,</span> size</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span>\n  array<span class=\"token punctuation\">.</span><span class=\"token function\">reduce</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">acc<span class=\"token punctuation\">,</span> _<span class=\"token punctuation\">,</span> i</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>i <span class=\"token operator\">%</span> size <span class=\"token operator\">===</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span> acc<span class=\"token punctuation\">.</span><span class=\"token function\">push</span><span class=\"token punctuation\">(</span>array<span class=\"token punctuation\">.</span><span class=\"token function\">slice</span><span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">,</span> i <span class=\"token operator\">+</span> size<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> acc\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// Usage:</span>\n<span class=\"token keyword\">const</span> numbers <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">2</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">,</span> <span class=\"token number\">4</span><span class=\"token punctuation\">,</span> <span class=\"token number\">5</span><span class=\"token punctuation\">,</span> <span class=\"token number\">6</span><span class=\"token punctuation\">,</span> <span class=\"token number\">7</span><span class=\"token punctuation\">,</span> <span class=\"token number\">8</span><span class=\"token punctuation\">,</span> <span class=\"token number\">9</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">]</span>\n<span class=\"token keyword\">const</span> chunked <span class=\"token operator\">=</span> <span class=\"token function\">chunk</span><span class=\"token punctuation\">(</span>numbers<span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span>\nconsole<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>chunked<span class=\"token punctuation\">)</span> <span class=\"token comment\">//=> [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]</span>\n</span></code></pre>\n<p>At every <em>nth</em> iteration (where <em>n</em> = <code>size</code>; starting at the first iteration),\nthe accumulator array (<code>acc</code>)\nis appended with a chunk of the array (<code>array.slice(i, i + size)</code>)\nand then returned.\nAt other iterations,\nthe accumulator array is returned as-is.</p>\n<p>If <code>size</code> is zero,\nthe method returns an empty array.\nIf <code>size</code> is negative,\nthe method returns broken results.\nSo, if needed in your case,\nyou may want to do something about negative or non-positive <code>size</code> values.</p>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<ul>\n<li><a href=\"https://flems.io/#0=N4IgtglgJlA2CmIBcBGADGgNCAZhBAzsgNqgB2AhmIkiAHQBWR2AxgPZkAu8XyI7ZApwAELABYBXMgGthAXmEAKCgCcVFAJ6ZhBCAC94ASnkA+ADplhw1eo10V8KBJbxFyli20B9bRGNyTYWALKysIHCUIYQBSHX14eTkFNGMKDzoABwkCMWU1TToCWAgXRQhfYQBqOINDQxDQh04JFUs0lgaAX21iAF16sgsAeiHhAFUCCgBzeCQLASFhMgkwACN4FQJ5YWIUbQAmbQBmbQAWbQBWbQA2bQB2bQAObQBObXRe+Y5F8SlpR22vxkimWaw2BGOAwWbAQdFgbCmiiB-ygAxA2AI8AQLE4EG+fBeSH2aBAnV6nSAA\" class=\"link link-external\">Test the code on flems.io</a></li>\n<li><a href=\"https://stackoverflow.com/a/60779547/1079869\" class=\"link link-external\">More info in my Stack Overflow answer</a></li>\n</ul>\n","summary":"A simple reducer\nto chunk (split) an array into smaller arrays.","date_published":"2021-01-01T12:00:00+03:00","date_modified":"2021-06-12T12:00:00+03:00","tags":["JavaScript"]},{"id":"https://mtsknn.fi/blog/how-to-bypass-episervers-content-delivery-api-when-doing-ajax-requests/","url":"https://mtsknn.fi/blog/how-to-bypass-episervers-content-delivery-api-when-doing-ajax-requests/","title":"How to bypass Episerver's Content Delivery API when doing Ajax requests","content_html":"<p>Requests with the <code>Accept</code> header set to <code>application/json</code>\nare automatically routed to Content Delivery API.\nHere's how to bypass it\nand make the request go to a page controller instead.</p>\n<p>Starting from Content Delivery API version 2.3.0,\nrequests with the <code>Accept</code> header set to <code>application/json</code>\nare automatically routed to Content Delivery API.\nThis is called\n<a href=\"https://world.episerver.com/documentation/developer-guides/content-delivery-api/using-friendly-urls/\" class=\"link link-external\">&quot;Using friendly URLs&quot; in the documentation</a>.</p>\n<p>This is a nice feature.\nBut what if you want to make an Ajax request to a page controller instead?\nFor example,\nif you have a Single Page Application,\nyou might want to retrieve the next page's data in JSON format\nfrom the page type's controller\nusing an Ajax request.</p>\n<p>In the controller\nyou can do something like this:</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">MyPageController</span> <span class=\"token punctuation\">:</span> <span class=\"token type-list\"><span class=\"token class-name\">PageController<span class=\"token punctuation\">&lt;</span>MyPage<span class=\"token punctuation\">></span></span></span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">public</span> <span class=\"token return-type class-name\">ActionResult</span> <span class=\"token function\">Index</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">MyPage</span> currentPage<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>Request<span class=\"token punctuation\">.</span><span class=\"token function\">IsAjaxRequest</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">{</span>\n            <span class=\"token class-name\"><span class=\"token keyword\">var</span></span> data <span class=\"token operator\">=</span> <span class=\"token comment\">/* ... */</span><span class=\"token punctuation\">;</span>\n            <span class=\"token keyword\">return</span> <span class=\"token function\">Json</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n\n        <span class=\"token keyword\">return</span> <span class=\"token function\">View</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Index\"</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\">MyPageViewModel</span><span class=\"token punctuation\">(</span>currentPage<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p><em>But</em>,\nbecause the Ajax request's <code>Accept</code> header will be set to <code>application/json</code>,\nthe request will be automatically routed to Content Delivery API,\nand the request will never reach the controller.</p>\n<p>The value of the <code>Accept</code> header\ncan't be changed to another <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types\" class=\"link link-external\">MIME type</a>\nif the desired format for the response data is JSON.\nSo what to do?</p>\n<p>Turns out that\n<strong>it's possible to specify\na <a href=\"https://developer.mozilla.org/en-US/docs/Glossary/Quality_values\" class=\"link link-external\"><em>relative quality value</em></a>\nfor the MIME type</strong>:\n<code>application/json;q=1</code>.\nFrom MDN:</p>\n<blockquote>\n<p><strong>Quality values</strong>,\nor <em>q-values</em> and <em>q-factors</em>,\nare used to describe the order of priority of values\nin a comma-separated list.\nIt is a special syntax allowed\nin some <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers\" class=\"link link-external\">HTTP headers</a>\nand in HTML.\nThe importance of a value is marked by the suffix <code>';q='</code>\nimmediately followed by a value between <code>0</code> and <code>1</code> included,\nwith up to three decimal digits,\nthe highest value denoting the highest priority.\nWhen not present, the default value is <code>1</code>.</p>\n</blockquote>\n<p>Now that the value of the <code>Accept</code> header\ndoesn't strictly equal to <code>application/json</code>,\nthe request is not routed to Content Delivery API.\nInstead, it reaches the controller.\nEgg-cellent!</p>\n<p>Since only one MIME type is specified,\nand since the default quality value is anyway <code>1</code>,\nappending <code>;q=1</code> to the <code>Accept</code> header's value\ndoesn't have any side effects.</p>\n<p>You should probably document this somewhere.\nFor example,\nif you are using <code>fetch()</code>,\nyou can write something like this:</p>\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">getPageData</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">url</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">headers</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token comment\">// Requests with the `Accept` header set to `application/json`</span>\n      <span class=\"token comment\">// are automatically routed to Content Delivery API.</span>\n      <span class=\"token comment\">// The `;q=1` here is a \"relative quality value\"</span>\n      <span class=\"token comment\">// and doesn't have any effects other than</span>\n      <span class=\"token comment\">// tricking the request to go to the controller</span>\n      <span class=\"token comment\">// instead of Content Delivery API.</span>\n      <span class=\"token literal-property property\">Accept</span><span class=\"token operator\">:</span> <span class=\"token string\">'application/json;q=1'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">then</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">response</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> response<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>This way some overly enthusiastic developer\nwon't remove the quality value\nthinking that it's unnecessary\n(since there's only one MIME type\nand since the default quality value is anyway <code>1</code>).</p>\n","summary":"Requests with the `Accept` header set to `application/json`\nare automatically routed to Content Delivery API.\nHere's how to bypass it\nand make the request go to a page controller instead.","date_published":"2020-12-23T12:00:00+03:00","tags":["C#","Episerver","JavaScript"]},{"id":"https://mtsknn.fi/blog/tfa-stands-for-the-featured-article/","url":"https://mtsknn.fi/blog/tfa-stands-for-the-featured-article/","title":"TFA stands for \"The Featured Article\"","content_html":"<p>And RTFA/RTFM stands for &quot;Read The Fine Article/Manual.&quot;\nWho likes profanity, anyway?</p>\n<p>Some people on sites like Reddit and Hacker News\ntend to use the abbreviation TFA\nto refer to the discussed article.\nIf the comment is otherwise\nnon-negative or even positive,\nI'm always baffled –\nwhy the hostility?\nWhy the profanity?</p>\n<p>Luckily,\nwith some quick googling,\nI found that the letter &quot;F&quot; in the abbreviations TFA, RTFA and RTFM\ncan be interpreted as &quot;fine&quot; or &quot;featured.&quot;\nGenius!\nNow I don't have to get triggered\nfrom reading other people's comments on the Internet.</p>\n<hr>\n<p>Bonus:\n<a href=\"https://developer.android.com/reference/android/util/Log\" class=\"link link-external\">Android's <code>Log</code> util</a>\nhas <code>Log.d()</code>, <code>Log.e()</code>, <code>Log.i()</code>, <code>Log.v()</code> and <code>Log.w()</code> methods\nfor different levels of logging:\ndebug, error, information, verbose and warning, respectively.\nAnd there's one more:\n<a href=\"https://developer.android.com/reference/android/util/Log#wtf(java.lang.String,%20java.lang.String)\" class=\"link link-external\"><code>Log.wtf()</code></a>\nto &quot;report an exception that should never happen.&quot;\nBut what does &quot;wtf&quot; mean here?\n<em>What a terrible failure.</em>\nTerrific!</p>\n","summary":"And RTFA/RTFM stands for \"Read The Fine Article/Manual.\"\nWho likes profanity, anyway?","date_published":"2020-12-01T12:00:00+03:00","tags":["English"]},{"id":"https://mtsknn.fi/blog/rfc-2119-in-a-nutshell/","url":"https://mtsknn.fi/blog/rfc-2119-in-a-nutshell/","title":"RFC 2119 in a nutshell","content_html":"<p>RFC 2119 defines key words such as\n&quot;MUST,&quot; &quot;MUST NOT,&quot; &quot;REQUIRED&quot; and &quot;SHALL&quot;\nthat are often used in specifications.\nHere's a summary of the key word definitions.</p>\n<p>Some specifications,\nlike <a href=\"https://sembr.org/\" class=\"link link-external\">Semantic Line Breaks</a>\nand <a href=\"https://semver.org/\" class=\"link link-external\">Semantic Versioning</a>,\ninclude this piece of text at the beginning –\nas RECOMMENDED by <a href=\"https://www.ietf.org/rfc/rfc2119.txt\" class=\"link link-external\">RFC 2119</a>:</p>\n<blockquote>\n<p>The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHALL&quot;, &quot;SHALL\nNOT&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;, &quot;RECOMMENDED&quot;, &quot;MAY&quot;, and\n&quot;OPTIONAL&quot; in this document are to be interpreted as described in\nRFC 2119.</p>\n</blockquote>\n<p>Here's a summary of what each of the key words mean:</p>\n<dl>\n<dt>MUST = REQUIRED = SHALL</dt>\n<dd>An absolute requirement.</dd>\n<dt>MUST NOT = SHALL NOT</dt>\n<dd>An absolute prohibition.</dd>\n<dt>SHOULD = RECOMMENDED</dt>\n<dd>Ignore only with good reasons.</dd>\n<dt>SHOULD NOT = NOT RECOMMENDED<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup></dt>\n<dd>Ignore only with good reasons.</dd>\n<dt>MAY = OPTIONAL</dt>\n<dd>Truly optional,\nbut if included,\nthe implementation MUST be interoperable\nwith another implementation\nwhich does not include the option,\nand vice versa.</dd>\n</dl>\n<p>The RFC also raises these points for specification authors:</p>\n<ul>\n<li>The key words MUST be used only\nwhen required\nfor interoperability\nor limiting potentially harmful behavior.</li>\n<li>The security implications\nbetween MUST and SHOULD,\nas well as between MUST NOT and SHOULD NOT,\nmay be very subtle,\nso the differences SHOULD<sup><a aria-label=\"footnote 2\" class=\"link\" href=\"#fn-2\" id=\"fnref-2\">[2]</a></sup> be elaborated.</li>\n</ul>\n<p>Now go\n<a href=\"https://www.ietf.org/rfc/rfc2119.txt\" class=\"link link-external\">read the RFC</a>,\nit's actually very short and clear.</p>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>The key word &quot;NOT RECOMMENDED&quot;\nis missing from the recommended introductory phrase,\nbut it's paired with &quot;SHOULD NOT&quot; in the RFC. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n<li id=\"fn-2\"><p>The word &quot;should&quot; is not capitalized in this part of the RFC,\nbut it seems like a missed opportunity\nso I capitalized it. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-2\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"RFC 2119 defines key words such as\n\"MUST,\" \"MUST NOT,\" \"REQUIRED\" and \"SHALL\"\nthat are often used in specifications.\nHere's a summary of the key word definitions.","date_published":"2020-11-17T12:00:00+03:00","date_modified":"2021-05-11T12:00:00+03:00","tags":["Miscellaneous"]},{"id":"https://mtsknn.fi/blog/hyperscript-hyperior-alternative-to-jsx/","url":"https://mtsknn.fi/blog/hyperscript-hyperior-alternative-to-jsx/","title":"Hyperscript: hyperior alternative to JSX","content_html":"<p>JSX is just JS under the hood.\nHyperscript is like that JS,\nbut better.\nExample:\n<code>h('a.link', { href }, 'Click me')</code>\nis nicer than\n<code>&lt;a className=&quot;link&quot; href={href}&gt;Click me&lt;/a&gt;</code>,\nright? Right?!</p>\n<h2 id=\"how-jsx-works\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#how-jsx-works\">How JSX works</a></h2>\n<p>JSX is not valid JS –\nit's a syntax extension to JS.\nLet's consider the following imaginary React component written using JSX:</p>\n<pre aria-label=\"Code block\" class=\"language-jsx bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">Header</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> links <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>header</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>my-header<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>header<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>logo<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">mtsknn.fi</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>nav</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>ul</span> <span class=\"token attr-name\">className</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>header__links<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n          </span><span class=\"token punctuation\">{</span>links<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> href<span class=\"token punctuation\">,</span> text <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>\n            <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>li</span> <span class=\"token attr-name\">key</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>href<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n              </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token script language-javascript\"><span class=\"token script-punctuation punctuation\">=</span><span class=\"token punctuation\">{</span>href<span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">></span></span><span class=\"token punctuation\">{</span>text<span class=\"token punctuation\">}</span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>li</span><span class=\"token punctuation\">></span></span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>li</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n            </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">DarkModeToggle</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token plain-text\">\n          </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>li</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n        </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>ul</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n      </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>nav</span><span class=\"token punctuation\">></span></span><span class=\"token plain-text\">\n    </span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>header</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>The code gets compiled by Babel before it's served to the browser<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup>,\nso that JSX gets replaced with <code>React.createElement()</code> function invocations<sup><a aria-label=\"footnote 2\" class=\"link\" href=\"#fn-2\" id=\"fnref-2\">[2]</a></sup>:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">function</span> <span class=\"token function\">Header</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> links <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    React<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'header'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">id</span><span class=\"token operator\">:</span> <span class=\"token string\">'my-header'</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">className</span><span class=\"token operator\">:</span> <span class=\"token string\">'header'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      React<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'div'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">className</span><span class=\"token operator\">:</span> <span class=\"token string\">'logo'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        React<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">href</span><span class=\"token operator\">:</span> <span class=\"token string\">'/'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'mtsknn.fi'</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      React<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'nav'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n        React<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ul'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">className</span><span class=\"token operator\">:</span> <span class=\"token string\">'header__links'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          links<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> href<span class=\"token punctuation\">,</span> text <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>\n            React<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'li'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">key</span><span class=\"token operator\">:</span> href <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              React<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">href</span><span class=\"token operator\">:</span> href <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> text<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          React<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span><span class=\"token string\">'li'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n            React<span class=\"token punctuation\">.</span><span class=\"token function\">createElement</span><span class=\"token punctuation\">(</span>DarkModeToggle<span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Uh, that's a bit annoying to read.\nLet's make that a bit more readable\nby assigning <code>React.createElement()</code> to a new variable <code>h</code>.\n(&quot;h&quot; as in hyperscript –\nthough note that this is not yet proper hyperscript.)\nLet's also simplify <code>{ href: href }</code> into <code>{ href }</code> because OCD:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> createElement <span class=\"token keyword\">as</span> h <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">Header</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> links <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'header'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">id</span><span class=\"token operator\">:</span> <span class=\"token string\">'my-header'</span><span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">className</span><span class=\"token operator\">:</span> <span class=\"token string\">'header'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'div'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">className</span><span class=\"token operator\">:</span> <span class=\"token string\">'logo'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">href</span><span class=\"token operator\">:</span> <span class=\"token string\">'/'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'mtsknn.fi'</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'nav'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ul'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">className</span><span class=\"token operator\">:</span> <span class=\"token string\">'header__links'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n          links<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> href<span class=\"token punctuation\">,</span> text <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>\n            <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'li'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">key</span><span class=\"token operator\">:</span> href <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n              <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> href <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> text<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'li'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span>\n            <span class=\"token function\">h</span><span class=\"token punctuation\">(</span>DarkModeToggle<span class=\"token punctuation\">,</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Now we can more clearly see that</p>\n<ul>\n<li>the first argument of <code>React.createElement()</code> is the tag name or component;</li>\n<li>the second argument is the <code>props</code> object\n(or <code>null</code> if the element doesn't get any props);</li>\n<li>and the rest of the arguments are the children of the element.</li>\n</ul>\n<p>So, under the hood, JSX is just regular JS.\nAnd it's not specific to React.\nBabel could as well replace JSX with something else than <code>React.createElement()</code> function invocations.</p>\n<h2 id=\"what-hyperscript-is-and-looks-like\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#what-hyperscript-is-and-looks-like\">What hyperscript is and looks like</a></h2>\n<p>I couldn't find a proper definition for hyperscript,\nbut think of it as the <strong>JS equivalent of JSX</strong>.\nYou can use hyperscript to <strong>declare the structure of your (React) components in plain JS</strong>.\nHyperscript is like the code in the previous code block, but better.</p>\n<p>Let's take a look at two real-world hyperscript implementations to see what they look like.</p>\n<h3 id=\"example-1-react-with-react-hyperscript\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-1-react-with-react-hyperscript\">Example 1: React with <code>react-hyperscript</code></a></h3>\n<p>Looking at our previous code example,\nit would be nice if we could at least:</p>\n<ul>\n<li>omit the second argument if it's <code>null</code>.\nThis would make the code easier to read by reducing visual noise.</li>\n<li>optionally wrap the children in an array.\nThis would make indentation more logical and Prettier-compatible (is that a word?).\nIt would also allow you to use trailing commas if that's your thing.</li>\n</ul>\n<p>The <a href=\"https://github.com/mlmorg/react-hyperscript\" class=\"link link-external\"><code>react-hyperscript</code> library</a>\nsupports that.\nIt also supports:</p>\n<ul>\n<li>using CSS selectors in the tag names to specify CSS classes and IDs.\nFor example,\n<code>h('header#my-id.foo.bar')</code>\nis the same as\n<code>h('header', { id: 'my-id', className: 'foo bar' })</code>.</li>\n<li>omitting the tag name if it's <code>div</code>,\ni.e. the default tag name is <code>div</code>.\nFor example,\n<code>h('.logo')</code>\nis the same as\n<code>h('div.logo')</code>.</li>\n</ul>\n<p>Let's see what our component looks like when using <code>react-hyperscript</code>:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> h <span class=\"token keyword\">from</span> <span class=\"token string\">'react-hyperscript'</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">Header</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> links <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'header#my-header.header'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'.logo'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">href</span><span class=\"token operator\">:</span> <span class=\"token string\">'/'</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'mtsknn.fi'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'nav'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ul.header__links'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n          links<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> href<span class=\"token punctuation\">,</span> text <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>\n            <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'li'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">key</span><span class=\"token operator\">:</span> href <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n              <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> href <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">h</span><span class=\"token punctuation\">(</span><span class=\"token string\">'li'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n            <span class=\"token function\">h</span><span class=\"token punctuation\">(</span>DarkModeToggle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>That looks pretty good!\nWe got rid of <code>null</code> props and some other prop objects as well by using CSS syntax.\nChildren that are not on the same line as their parent\nare wrapped in arrays.\nThis makes the indentation clearer.</p>\n<p>(Side note:\nat the end of the code example,\nthe <code>DarkModeToggle</code> component needs to be in an array because\nit's a component instead of a string.\nThere's an <a href=\"https://github.com/mlmorg/react-hyperscript/issues/7\" class=\"link link-external\">open issue on GitHub</a> about that.\nUnfortunately the library hasn't received any updates in a couple of years,\nbut this issue is not a biggie.)</p>\n<p>To start using <code>react-hyperscript</code>,</p>\n<ol>\n<li>run <code>npm install react-hyperscript</code>\n(or <code>npm i react-hyperscript</code> for short)</li>\n<li>add <code>import h from 'react-hyperscript'</code> to your code</li>\n<li>start replacing that filthy JSX with the <code>h()</code> function.</li>\n</ol>\n<h3 id=\"example-2-mithril\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-2-mithril\">Example 2: Mithril</a></h3>\n<p><a href=\"https://mithril.js.org/\" class=\"link link-external\">Mithril</a>\nis a small (&lt;10kb gzip) yet mighty JS framework.\nI prefer using Mithril over React in my own projects because it's so good.\n(I should write a blog post about it.)</p>\n<p>Anyway, Mithril uses hyperscript by default.\nMithril's hyperscript flavor has two main extra features compared to <code>react-hyperscript</code>:</p>\n<ul>\n<li>Besides CSS classes and IDs,\nyou can also specify static attributes\nin the tag name (first parameter)\nusing CSS syntax.\nFor example,\n<code>m('a[href=/foo]')</code> is the same as <code>m('a', { href: '/foo' })</code>.\n(Notice how Mithril uses <code>m()</code> instead of <code>h()</code>,\nbut that doesn't matter in practice.)</li>\n<li>It's more flexible about its input parameters –\nchildren can be in an array,\nbut they don't have to.\nIn our header component example,\nthe <code>DarkModeToggle</code> component doesn't need to be in an array\n(unlike when using <code>react-hyperscript</code>).</li>\n</ul>\n<p>Our component would look like this when using Mithril:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-js bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span><br /><br /><br /><br /><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">import</span> m <span class=\"token keyword\">from</span> <span class=\"token string\">'mithril'</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">Header</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> links <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'header#my-header.header'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'.logo'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a[href=/]'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'mtsknn.fi'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'nav'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n        <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'ul.header__links'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n          links<span class=\"token punctuation\">.</span><span class=\"token function\">map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\"><span class=\"token punctuation\">{</span> href<span class=\"token punctuation\">,</span> text <span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">(</span>\n            <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'li'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">key</span><span class=\"token operator\">:</span> href <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n              <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'a'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> href <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> text<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n            <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n          <span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n          <span class=\"token function\">m</span><span class=\"token punctuation\">(</span><span class=\"token string\">'li'</span><span class=\"token punctuation\">,</span> <span class=\"token function\">m</span><span class=\"token punctuation\">(</span>DarkModeToggle<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>That's almost identical than when using <code>react-hyperscript</code>,\nonly a bit more terse.\n(I have highlighted the two lines that are different.)\nBut being able to specify static attributes using CSS syntax is nifty,\nespecially when you have more of them;\nthe example header component has just one.\nAnd the increased flexibility regarding the input parameters\nmeans one thing less to worry about,\ni.e. whether children need to be wrapped in arrays or not.</p>\n<p>Since Mithril uses hyperscript by default,\nthere's no steps to enable hyperscript.\nRead the <a href=\"https://mithril.js.org/simple-application.html\" class=\"link link-external\">Mithril tutorial</a>\nand start using it.</p>\n<h2 id=\"pros-and-cons-of-hyperscript\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#pros-and-cons-of-hyperscript\">Pros and cons of hyperscript</a></h2>\n<p>Okay, that's nice and all,\nbut why would you use hyperscript over JSX?</p>\n<h3 id=\"pros\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#pros\">Pros</a></h3>\n<p>Here's a non-exhaustive list of the good sides of hyperscript:</p>\n<ul>\n<li><strong>No need for context switching between JS and XML</strong>\nbecause hyperscript is just JavaScript.\n<ul>\n<li>Hyperscript could also make it easier to understand\nhow your JS framework of choice (be it React or Mithril or something else) works.\nWhen learning React and JSX,\na possibly common frustration is\n&quot;why can't I use <code>if</code> statements and <code>for</code> loops in my JSX?&quot;\nWhen using hyperscript,\nit's probably clearer\nwhy you can't have <code>if</code> statements and <code>for</code> loops as function arguments.</li>\n</ul>\n</li>\n<li><strong>CSS classes and IDs can be specified in the tag name</strong>\nwhich leads to terser code.\nVery nice when using e.g. <a href=\"https://tailwindcss.com/\" class=\"link link-external\">Tailwind</a>:\n<code>h('img.h-16.md:h-24')</code> etc.\n<ul>\n<li>In some flavors of hyperscript (e.g. Mithril),\nother CSS-like shorthands are also possible,\nlike <code>m('a[href=/foo].bar.baz')</code>.</li>\n</ul>\n</li>\n<li>Hyperscript is <strong>less verbose</strong> (CSS-like tag names, no closing tags).\nThis leads to shorter code –\nwithout sacrificing readability if you ask me.</li>\n<li><strong>No need for JSX compilation.</strong>\nThis leads to simpler tooling,\nsimpler build processes\nand fewer dependencies.\n<ul>\n<li>You can start prototyping\nwithout having to setup complex build processes et cetera.\n(Though in the case of React,\nusing Create React App is also easy and quick.\nBut still much more complex!)</li>\n<li>Simpler tooling also means that\nyour code editor doesn't have to support or have plugins for\nJSX parsing, highlighting etc.\nJust open <code>notepad.exe</code> and start hacking\nlike any advanced programmer.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"cons\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#cons\">Cons</a></h3>\n<ul>\n<li>\n<p>Hyperscript is <strong>not as common as JSX</strong> (at least in the React world).\nUnless you can convince your team to choose hyperscript,\nJSX is likely the easier choice.\nIf you use JSX,\nit might also be easier to\nattract new developers to your team\ndue to familiarity.\nThough learning hyperscript shouldn't be at all difficult.</p>\n</li>\n<li>\n<p><strong>The syntax might take some time to get used to.</strong>\nWhile hyperscript is just JavaScript,\nthere are a lot of different kinds of brackets.\nBut like said:\nlearning hyperscript shouldn't be difficult.</p>\n</li>\n<li>\n<p>Because there aren't closing tags like in JSX,\nyou <strong>can't see as well where tags end</strong>.\nI haven't personally found this to be a problem though.</p>\n<ul>\n<li>\n<p>Android Studio solves this problem nicely for Flutter projects.\nIt automatically shows code comments\nat the end of widgets (think of React components)\nlike this:</p>\n<pre aria-label=\"Code block\" class=\"language-dart bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">MyWidget</span><span class=\"token punctuation\">(</span>\n  child<span class=\"token punctuation\">:</span> <span class=\"token class-name\">Container</span><span class=\"token punctuation\">(</span>\n    color<span class=\"token punctuation\">:</span> <span class=\"token class-name\">Colors</span><span class=\"token punctuation\">.</span>pinkAccent<span class=\"token punctuation\">,</span>\n    height<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">,</span>\n    width<span class=\"token punctuation\">:</span> <span class=\"token number\">200</span><span class=\"token punctuation\">,</span>\n    child<span class=\"token punctuation\">:</span> <span class=\"token class-name\">Center</span><span class=\"token punctuation\">(</span>\n      child<span class=\"token punctuation\">:</span> <span class=\"token class-name\">Text</span><span class=\"token punctuation\">(</span>\n        <span class=\"token string-literal\"><span class=\"token string\">'Bacon ipsum'</span></span><span class=\"token punctuation\">,</span>\n        style<span class=\"token punctuation\">:</span> <span class=\"token class-name\">TextStyle</span><span class=\"token punctuation\">(</span>\n          color<span class=\"token punctuation\">:</span> <span class=\"token class-name\">Colors</span><span class=\"token punctuation\">.</span>white<span class=\"token punctuation\">,</span>\n          fontSize<span class=\"token punctuation\">:</span> <span class=\"token number\">24</span><span class=\"token punctuation\">,</span>\n          fontWeight<span class=\"token punctuation\">:</span> <span class=\"token class-name\">FontWeight</span><span class=\"token punctuation\">.</span>bold<span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// Text</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// Center</span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// Container</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// MyWidget</span>\n</span></code></pre>\n</li>\n</ul>\n</li>\n<li>\n<p>There's <strong>runtime overhead</strong> of parsing the CSS-like selectors.\nThis is unlikely a major performance problem\nunless you have lots of complex selectors,\nbut something to be aware of.</p>\n<ul>\n<li>If you use Mithril,\nyou can setup\na <a href=\"https://github.com/mithriljs/mopt\" class=\"link link-external\">Babel plugin called <code>mopt</code></a>\nto statically optimize <code>m()</code> function invocations.\nLook ma, no more runtime overhead!</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"bottom-line\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#bottom-line\">Bottom line</a></h3>\n<p>Hmm, looks I listed as many pros as I listed cons.\nDidn't I say that hyperscript is hyperior?!</p>\n<p>If you ask me, the pros greatly outweight the cons.\nGive my hyperscript over JSX any day, thank you.</p>\n<p>In the end it's a matter of personal preference\nand what works for you and your team.\nBecause this is a personal blog,\nI'm free to call hyperscript hyperior anyhow,\nso checkmate, atheists. ;-)</p>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<ul>\n<li>The React docs have a couple of pages about JSX:\n<ul>\n<li><a href=\"https://reactjs.org/docs/introducing-jsx.html\" class=\"link link-external\">Introducing JSX</a></li>\n<li><a href=\"https://reactjs.org/docs/jsx-in-depth.html\" class=\"link link-external\">JSX In Depth</a></li>\n<li><a href=\"https://reactjs.org/docs/react-without-jsx.html\" class=\"link link-external\">React Without JSX</a></li>\n</ul>\n</li>\n<li><a href=\"https://mithril.js.org/jsx.html#jsx-vs-hyperscript\" class=\"link link-external\">Mithril docs' section about JSX vs hyperscript</a></li>\n<li><a href=\"https://github.com/ohanhi/hyperscript-helpers\" class=\"link link-external\"><code>hyperscript-helpers</code></a>\nand\n<a href=\"https://github.com/Jador/react-hyperscript-helpers\" class=\"link link-external\"><code>react-hyperscript-helpers</code></a>\noffer even terser hyperscript syntax.\nI'm not a fan of this style,\nbut check them out,\nyou might like them.</li>\n<li><a href=\"https://github.com/thi-ng/umbrella/tree/develop/packages/hdom\" class=\"link link-external\"><code>@thi.ng/hdom</code></a>\nand <a href=\"https://github.com/lukejacksonn/ijk\" class=\"link link-external\"><code>ijk</code></a>\nare hyperscript alternatives based on arrays.\nI haven't tried these,\nbut they look interesting.</li>\n</ul>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>It's also possible to use Babel in the browser to compile code on the fly.\nThat would be slow and in most cases just unnecessary. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n<li id=\"fn-2\"><p><a href=\"https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html\" class=\"link link-external\">React 17 introduced a new JSX transform</a>\nthat makes the output look different.\nThis doesn't change the idea behind hyperscript. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-2\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"JSX is just JS under the hood.\nHyperscript is like that JS, but better.","date_published":"2020-11-03T12:00:00+03:00","date_modified":"2020-12-27T12:00:00+03:00","tags":["JavaScript","Mithril.js","React"]},{"id":"https://mtsknn.fi/blog/contenteditable-first-letter-chrome-buggy-combo/","url":"https://mtsknn.fi/blog/contenteditable-first-letter-chrome-buggy-combo/","title":"`contenteditable` + `::first-letter` + Chrome = buggy combo","content_html":"<p>This combination causes weird behavior and data loss\nbecause of a bug in Chrome,\nso it's best not to use <code>contenteditable</code> and <code>::first-letter</code> together.</p>\n<h2 id=\"whats-contenteditable\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#whats-contenteditable\">What's <code>contenteditable</code>?</a></h2>\n<p>The <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable\" class=\"link link-external\"><code>contenteditable</code> HTML attribute</a>\nmakes an element's contents editable in the browser.</p>\n<p>Demo:</p>\n<blockquote contenteditable><p>I'm a blockquote with the `contenteditable` attribute. Select and edit me!</p></blockquote>\n<p>For example,\n<a href=\"https://www.tiny.cloud/\" class=\"link link-external\">TinyMCE</a>\nis a WYSIWYG (&quot;what you see is what you get&quot;) HTML editor\nthat uses the <code>contenteditable</code> attribute under the hood.</p>\n<h2 id=\"whats-first-letter\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#whats-first-letter\">What's <code>::first-letter</code>?</a></h2>\n<p>The <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/::first-letter\" class=\"link link-external\"><code>::first-letter</code> CSS pseudo-element</a>\ncan be used to stylize\n&quot;the first letter of the first line of a block-level element,\nbut only when not preceded by other content (such as images or inline tables).&quot;</p>\n<p><a href=\"https://en.wikipedia.org/wiki/Initial\" class=\"link link-external\"><em>Initial</em> on Wikipedia</a>:</p>\n<blockquote>\n<p>In a written or published work,\nan <strong>initial</strong> or <strong>drop cap</strong>\nis a letter at the beginning of a word, a chapter, or a paragraph\nthat is larger than the rest of the text.</p>\n</blockquote>\n<p>Example:</p>\n<pre aria-label=\"Code block\" class=\"language-css bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token selector\">article > p:first-child::first-letter</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token property\">font-size</span><span class=\"token punctuation\">:</span> 2em<span class=\"token punctuation\">;</span>\n  <span class=\"token property\">font-weight</span><span class=\"token punctuation\">:</span> bold<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Demo:</p>\n<blockquote class=\"drop-cap-demo\">\n<p>This paragraph has an initial / drop cap letter,\nmeaning that the first letter (&quot;T&quot;) should be large and bold.</p>\n<p>This paragraph has a regular first letter.</p>\n</blockquote>\n<h2 id=\"whats-chrome\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#whats-chrome\">What's Chrome?</a></h2>\n<p>Just kidding, I know you know. 😄</p>\n<h2 id=\"example-use-case-for-the-combination\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#example-use-case-for-the-combination\">Example use case for the combination</a></h2>\n<ul>\n<li>Your website shows drop cap letters (<code>::first-letter</code>) somewhere.</li>\n<li>Your website's content creators create content with TinyMCE\n(which uses the <code>contenteditable</code> attribute under the hood).</li>\n<li>The TinyMCE editor has been stylized (using CSS) to show drop cap letters\nso that the content looks the same in the TinyMCE editor and on the live site.</li>\n<li>Your website's content creators use Chrome.</li>\n</ul>\n<p>That's how you end up with the buggy combination that's\n<code>contenteditable</code> + <code>::first-letter</code> + Chrome.</p>\n<h2 id=\"problems-with-the-combination\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#problems-with-the-combination\">Problems with the combination</a></h2>\n<p>Note:\nin my case I had a TinyMCE editor\nin which the first paragraph had a drop cap letter\nand other paragraphs were normal (no drop caps).\nIn other cases the problems might be different.</p>\n<p>Also note:\nthe problems are not related to TinyMCE per se;\nit just happens to use the <code>contenteditable</code> attribute under the hood.</p>\n<h3 id=\"incorrect-caret-position\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#incorrect-caret-position\">Incorrect caret position</a></h3>\n<p>When the caret is in the drop cap paragraph,\nthe caret's position is one character too much to the right.\nThis is confusing.</p>\n<p><strong>Update in November 2021:</strong>\nthis seems to no longer be an issue as of Chrome 95\n(though I don't know in which version the issue has been fixed).</p>\n<h3 id=\"minor-data-loss\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#minor-data-loss\">Minor data loss</a></h3>\n<p>When the caret is at the end of the first paragraph,\npressing <kbd>Delete</kbd> <strong>removes two characters</strong>:</p>\n<ul>\n<li>the line break between the paragraphs</li>\n<li>the last character of the first paragraph.</li>\n</ul>\n<p>Normally only the line break between the paragraphs should be removed,\nso that the contents of the second paragraph\nare moved to the end of the first paragraph.</p>\n<p>The same issue happens (two characters are removed instead of one)\nwhen the caret is at the beginning of the second paragraph\nand <kbd>Backspace</kbd> is pressed.</p>\n<p>In addition to causing minor data loss,\nthis buggy behavior is also confusing.</p>\n<p>Extra confusion:\nif the drop cap covers more than one character<sup><a aria-label=\"footnote 1\" class=\"link\" href=\"#fn-1\" id=\"fnref-1\">[1]</a></sup>,\nthe operations above remove more than one extra character.</p>\n<h3 id=\"major-data-loss\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#major-data-loss\">Major data loss</a></h3>\n<p>When the caret is at the end of the drop cap paragraph,\npressing <kbd>Backspace</kbd> <strong>empties the whole paragraph</strong>!\nLooking at Chrome DevTools,\nthe <code>&lt;p&gt;</code> tag is still there,\nbut it's empty.</p>\n<p>Normally only the last character of the paragraph should be removed.</p>\n<p>The same issue happens (the whole paragraph is emptied)\nwhen the caret is at the second last position of the first paragraph\nand <kbd>Delete</kbd> is pressed.</p>\n<p>In addition to causing major data loss,\nthis buggy behavior is also confusing.</p>\n<h2 id=\"the-root-cause-a-bug-in-chrome\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-root-cause-a-bug-in-chrome\">The root cause: a bug in Chrome</a></h2>\n<p>Long story short:\nthis is a bug in Chrome.\nThe <a href=\"https://bugs.chromium.org/p/chromium/issues/detail?id=174349\" class=\"link link-external\">&quot;Contenteditable with :first-letter styling&quot; Chromium issue</a>\nwas reported in 2013 (almost 9 years ago as of November 2021)\nand hasn't been fixed yet.</p>\n<h2 id=\"the-fix-dont-use-the-combination\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-fix-dont-use-the-combination\">The fix: don't use the combination</a></h2>\n<p>In practice you should avoid using\nthe <code>contenteditable</code> HTML attribute\nand the <code>::first-letter</code> CSS pseudo-element together.</p>\n<p>Avoiding the bug might not be a &quot;fix&quot; per se,\nbut since the bug is in Chrome itself,\nthere's not much else to do.</p>\n<h2 id=\"how-i-investigated-the-issue\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#how-i-investigated-the-issue\">How I investigated the issue</a></h2>\n<p>This was a simple 30-minute investigation,\nbut I hope that this short story will be helpful to some\nby showing one way of investigating these kind of bugs.</p>\n<ol>\n<li>\n<p>I first tested\nwhat styles of the <code>::first-letter</code> pseudo-element cause the problems\nby tweaking the styles of the buggy TinyMCE editor in Chrome DevTools.\nAfter quick fiddling,\nit was apparent that any single valid style causes the problems.\nBecause of this,\nit looked unlikely that the issue was in the CSS styles.</p>\n</li>\n<li>\n<p>Thinking that this was an issue in TinyMCE,\nI googled for queries like &quot;tinymce first-letter&quot;\nto look for bug reports and solutions.\nBut all I could find was\n<a href=\"https://github.com/tinymce/tinymce/issues/2891\" class=\"link link-external\">one TinyMCE issue on GitHub from 2016</a>\nwhich was closed in 2018 without a solution.\n&quot;It seems to be a problem with CSS and not TinyMCE itself&quot; they said.</p>\n</li>\n<li>\n<p>Hmm, what could I try next...\nI was using TinyMCE version 4 and the latest was version 5.\nSo maybe the issue had been fixed ages ago (in TinyMCE version 5)\nand everyone had already upgraded to version 5,\nso that's why no one was talking about this?\nNote that I was still thinking that the issue was in TinyMCE.</p>\n<p>Well, let's try.\nI remembered that there was a live demo of TinyMCE\n<a href=\"https://www.tiny.cloud/\" class=\"link link-external\">on TinyMCE's homepage</a>\n(update: the demos are not anymore on the homepage as of November 2021).\nI added drop cap stylings to the demo using Chrome DevTools, and...\nBleh, version 5 had the same problems.</p>\n</li>\n<li>\n<p>But wait!\nWhile tweaking the styles,\nI noticed that the TinyMCE demo used the <code>contenteditable</code> attribute.\nCould it be that this was not a styling issue\n(despite the GitHub issue suggesting otherwise)\nbut related to the <code>contenteditable</code> attribute?</p>\n<p>I created a\n<a href=\"https://flems.io/#0=N4IgzgpgNhDGAuEAmIBcIB0ALeBbKIANCAGYCWMYaA2qAHYCGuEamO+RIsA9nYn6wA8YeAE8YAPgA6dAASyAxACdu3eLOAz58gEbclSCEtSyAjAAcAHrLDcoZJLKxrzZOgGsA3Fu3mGSJDcAcxMABjMIXG85WQBfH2VVdQlZc1RyJREAWlgsCiRUdLJM+CyYeEQlDR95El5SkiYKURMwBjowLMglMhJo7Vk6vi6yAC8IEwAmSP7tIdKAdwgyIJwTPSgkWfi6QQB6EXEIaV3AgDdZHj4Ia8D4Bh0YWQcAXikQFTV3k-lBcwkAKJ3WTMACE+3+Pj+gOBCmY8FUEJO+3OEhAsQAurEgA\" class=\"link link-external\">quick 2-minute fiddle of the <code>contenteditable</code> + <code>::first-letter</code> combo at flems.io</a> –\nand voilà!\nMy quick fiddle had the same problems.</p>\n</li>\n<li>\n<p>Because the <code>contenteditable</code> attribute is more common than TinyMCE,\nI now had higher hopes of finding relevant results.\nWhen googling for &quot;contenteditable first-letter,&quot;\n<a href=\"https://stackoverflow.com/q/18397450/1079869\" class=\"link link-external\">the first SO question that I opened</a>\nhad this snippet of wisdom in the top-voted answer by Marc J (thanks Marc!):</p>\n<blockquote>\n<p>I just noticed that there is a\n<a href=\"https://bugs.chromium.org/p/chromium/issues/detail?id=174349\" class=\"link link-external\">bug in Chrome</a>\nthat causes the cursor to become in the wrong spot\nwith <code>contenteditable</code> and <code>::first-letter</code>.</p>\n</blockquote>\n</li>\n</ol>\n<p>And that was it!\nSome fiddling, googling, and a little bit of luck.</p>\n\n  <hr aria-hidden=\"true\">\n  <section aria-label=\"Footnotes\">\n    <h2 class=\"!text-base !text-gray-700 tracking-widest uppercase xl:!text-lg\">\n      Footnotes\n    </h2>\n    <ol>\n<li id=\"fn-1\"><p>If a drop cap paragraph starts e.g. with the word &quot;I'm,&quot;\nthe drop cap covers both the letter &quot;I&quot; and the apostrophe.\n&quot;The first letter of an element is not always trivial to identify,&quot;\nas mentioned on the <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/::first-letter\" class=\"link link-external\"><code>::first-letter</code> page on MDN</a>. <a aria-label=\"Back to reference\" class=\"font-mono link\" href=\"#fnref-1\">↩</a></p>\n</li>\n</ol>\n</section>\n","summary":"This combination causes weird behavior and data loss\nbecause of a bug in Chrome,\nso it's best not to use `contenteditable` and `::first-letter` together.","date_published":"2020-10-13T12:00:00+03:00","date_modified":"2021-11-30T12:00:00+03:00","tags":["CSS"]},{"id":"https://mtsknn.fi/blog/how-to-change-modified-date-programmatically-in-episerver/","url":"https://mtsknn.fi/blog/how-to-change-modified-date-programmatically-in-episerver/","title":"How to change Modified date programmatically in Episerver","content_html":"<p>When migrating content from another platform to Episerver,\nI wanted to set a specific datetime for each content's Modified date (<code>Saved</code> property).\nSounds simple,\nbut it was only possible by using Episerver's internal, undocumented API.</p>\n<h2 id=\"how-to-do-it\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#how-to-do-it\">How to do it</a></h2>\n<h3 id=\"setting-the-current-date\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#setting-the-current-date\">Setting the current date</a></h3>\n<p>This should be straightforward.</p>\n<ol>\n<li>Set a content's <code>SetChangedOnPublish</code> property to <code>true</code>:<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token punctuation\">(</span>content <span class=\"token keyword\">as</span> <span class=\"token class-name\">IChangeTrackable</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>SetChangedOnPublish <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n</li>\n<li>Publish the content.</li>\n</ol>\n<p>From\n<a href=\"https://support.optimizely.com/hc/en-us/articles/115002200452-Cannot-programmatically-set-modified-date\" class=\"link link-external\">Episerver's support page <em>Cannot programmatically set modified date</em></a>:</p>\n<blockquote>\n<p>[S]etting the <code>SetChangedOnPublish</code> property to <code>true</code>\nand then [publishing] the page\n[will] trigger an update of the modified date in the database.\nSo it will set the date to the same one as when you publish the page.</p>\n</blockquote>\n<h3 id=\"setting-an-arbitrary-date\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#setting-an-arbitrary-date\">Setting an arbitrary date</a></h3>\n<p>This is more complicated\nand this is what this blog post is about.</p>\n<p>First, here's the code:</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">using</span> <span class=\"token namespace\">EPiServer<span class=\"token punctuation\">.</span>Framework</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">using</span> <span class=\"token namespace\">EPiServer<span class=\"token punctuation\">.</span>DataAccess<span class=\"token punctuation\">.</span>Internal</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// ...</span>\n\n<span class=\"token comment\">// Update the \"Modified\" datetime.</span>\n<span class=\"token comment\">// A hacky solution that uses Epi's internal, undocumented, possibly unstable API.</span>\n<span class=\"token comment\">// Tested with EPiServer.CMS 11.14.0 and 11.19.0.</span>\nContextCache<span class=\"token punctuation\">.</span>Current<span class=\"token punctuation\">[</span>ContentSaveDB<span class=\"token punctuation\">.</span>UseIChangeTrackingSavedKey<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\"><span class=\"token keyword\">object</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">(</span>content <span class=\"token keyword\">as</span> <span class=\"token class-name\">IChangeTrackable</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>Saved <span class=\"token operator\">=</span> data<span class=\"token punctuation\">.</span>PublishedAt<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">(</span>content <span class=\"token keyword\">as</span> <span class=\"token class-name\">IChangeTrackable</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>SetChangedOnPublish <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n<p>And here's a rough explanation:</p>\n<ol>\n<li>Use Episerver's internal <code>ContentSaveDB</code> API\nto change an internal setting.\n(Or something like that –\nI don't really know what's going on\nsince the internal API is internal and undocumented).</li>\n<li>Set the content's <code>Saved</code> property to your desired datetime value.\n(Yes, the <code>Saved</code> property is the correct one\neven though we are modifying the Modified date.\nThere's no <code>Modified</code> property.)</li>\n<li>Set the content's <code>SetChangedOnPublish</code> property to <code>true</code>.\nOtherwise the new value of the <code>Saved</code> property won't take effect.\n(Yes, it's <code>SetChanged*</code>\nand not &quot;<code>SetSaved*</code>&quot; which doesn't even exist.\nI guess naming things is difficult.)</li>\n<li>That's it!\nNow publish the content\nor do whatever you were doing.</li>\n</ol>\n<p>But before you go,\nremember this:</p>\n<p>The code might stop working when you do any NuGet package updates.\nThat's because the API you are using is internal and thus unsupported.\nFrom the\n<a href=\"https://world.optimizely.com/csclasslibraries/cms/EPiServer.DataAccess.Internal.ContentSaveDB?version=11\" class=\"link link-external\"><code>ContentSaveDB</code> class's documentation</a>:</p>\n<blockquote>\n<p>Unsupported INTERNAL API! Not covered by semantic versioning; might change without notice.</p>\n</blockquote>\n<h2 id=\"is-this-a-good-idea\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#is-this-a-good-idea\">Is this a good idea?</a></h2>\n<p>Not really,\nbut do you have any better ideas?</p>\n<p>In my case,\nI was migrating content\nfrom another platform (Contentful, but that's irrelevant)\nto Episerver.\nAfter I was done,\nI didn't need the code anymore,\nso it didn't matter\nthat I was using an internal, undocumented, possibly unstable API.</p>\n<p>You too shouldn't use this hacky method in any long-living code.</p>\n<h2 id=\"should-this-be-even-possible\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#should-this-be-even-possible\">Should this be even possible?</a></h2>\n<p>Well, that's a funny question!\nAccording to\n<a href=\"https://support.optimizely.com/hc/en-us/articles/115002200452-Cannot-programmatically-set-modified-date\" class=\"link link-external\">Episerver's support page <em>Cannot programmatically set modified date</em></a>\nfrom October 2017,\nthis shouldn't be possible.\nFrom the page:</p>\n<div data-nosnippet>\n<blockquote>\n<p>It is <strong>not possible</strong> to set the modified date to a specific date programmatically.</p>\n<p><strong>The date can only be changed by setting the SetChangedOnPublish property to true and then publish the page.</strong>\nThis will in turn trigger an update of the modified date in the database.\nSo it will set the date to the same one as when you publish the page.</p>\n</blockquote>\n</div>\n<p>But like I explained above,\nit <strong>is</strong> possible to set the Modified date to a specific date programmatically.</p>\n<p>(Fun fact:\nGoogle decided to choose the misleading quote above\nas a Featured snipped\n(screenshot below).\nThanks Google. 😂\nI have since wrapped the <code>&lt;blockquote&gt;</code> in a <code>&lt;div data-nosnippet&gt;</code> element.)</p>\n<p class=\"max-w-none\"><img src=\"./google-featured-snippet.png\" alt=\"Screenshot of Google's Featured snippet from this blog post, displaying the above quote from Episerver's support page saying that it's not possible to set the modified date programmatically.\"></p>\n<h2 id=\"how-i-found-the-solution\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#how-i-found-the-solution\">How I found the solution</a></h2>\n<p>&quot;Oh, damn,&quot;\nI thought to myself after reading that Episerver's support page.\nBut fortunately I didn't give up just yet.</p>\n<p>After googling a bit more,\nI found\na <a href=\"https://gist.github.com/kimgunnarsson/b99f041421bcfb3efa376df698e7ad35\" class=\"link link-external\">GitHub gist by Kim Gunnarsson</a>\nfrom April 2016.\n(Funnily, the gist was created 1.5 years <em>before</em> that Episerver's support page.)</p>\n<p>The following lines of code from the gist are relevant,\nthe first line being the most important:</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">EPiServer<span class=\"token punctuation\">.</span>Framework<span class=\"token punctuation\">.</span>ContextCache<span class=\"token punctuation\">.</span>Current<span class=\"token punctuation\">[</span>EPiServer<span class=\"token punctuation\">.</span>DataAccess<span class=\"token punctuation\">.</span>ContentSaveDB<span class=\"token punctuation\">.</span>UseIChangeTrackingSavedKey<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\"><span class=\"token keyword\">object</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token class-name\"><span class=\"token keyword\">var</span></span> clone <span class=\"token operator\">=</span> page<span class=\"token punctuation\">.</span><span class=\"token function\">CreateWritableClone</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nclone<span class=\"token punctuation\">.</span>Saved <span class=\"token operator\">=</span> clone<span class=\"token punctuation\">.</span>StartPublish<span class=\"token punctuation\">;</span>\nclone<span class=\"token punctuation\">.</span>SetChangedOnPublish <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n<p>The gist is currently 4.5 years old (as of October 2020)\nand thus didn't work out of the box for me –\nI was using <code>EPiServer.CMS</code> version 11.14.0\nand soon after version 11.19.0.\nBut this did work:</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">EPiServer<span class=\"token punctuation\">.</span>Framework<span class=\"token punctuation\">.</span>ContextCache<span class=\"token punctuation\">.</span>Current<span class=\"token punctuation\">[</span>EPiServer<span class=\"token punctuation\">.</span>DataAccess<span class=\"token punctuation\">.</span>Internal<span class=\"token punctuation\">.</span>ContentSaveDB<span class=\"token punctuation\">.</span>UseIChangeTrackingSavedKey<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\"><span class=\"token keyword\">object</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">(</span>content <span class=\"token keyword\">as</span> <span class=\"token class-name\">IChangeTrackable</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>Saved <span class=\"token operator\">=</span> data<span class=\"token punctuation\">.</span>PublishedAt<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">(</span>content <span class=\"token keyword\">as</span> <span class=\"token class-name\">IChangeTrackable</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>SetChangedOnPublish <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n<p>I.e. the <code>ContentSaveDB</code> class has been changed into an internal class at some point.</p>\n<p>The first line can be simplified into this with proper <code>using</code> statements:</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">using</span> <span class=\"token namespace\">EPiServer<span class=\"token punctuation\">.</span>Framework</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">using</span> <span class=\"token namespace\">EPiServer<span class=\"token punctuation\">.</span>DataAccess<span class=\"token punctuation\">.</span>Internal</span><span class=\"token punctuation\">;</span>\n\nContextCache<span class=\"token punctuation\">.</span>Current<span class=\"token punctuation\">[</span>ContentSaveDB<span class=\"token punctuation\">.</span>UseIChangeTrackingSavedKey<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\"><span class=\"token keyword\">object</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n<p>Finally,\nyou may want to slap a comment saying that this is a hacky solution.\nBehold,\nwe ended up with the code that I already presented to you at the very beginning:</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">using</span> <span class=\"token namespace\">EPiServer<span class=\"token punctuation\">.</span>Framework</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">using</span> <span class=\"token namespace\">EPiServer<span class=\"token punctuation\">.</span>DataAccess<span class=\"token punctuation\">.</span>Internal</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// ...</span>\n\n<span class=\"token comment\">// Update the \"Modified\" datetime.</span>\n<span class=\"token comment\">// A hacky solution that uses Epi's internal, undocumented, possibly unstable API.</span>\n<span class=\"token comment\">// Tested with EPiServer.CMS 11.14.0 and 11.19.0.</span>\nContextCache<span class=\"token punctuation\">.</span>Current<span class=\"token punctuation\">[</span>ContentSaveDB<span class=\"token punctuation\">.</span>UseIChangeTrackingSavedKey<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token constructor-invocation class-name\"><span class=\"token keyword\">object</span></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">(</span>content <span class=\"token keyword\">as</span> <span class=\"token class-name\">IChangeTrackable</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>Saved <span class=\"token operator\">=</span> data<span class=\"token punctuation\">.</span>PublishedAt<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">(</span>content <span class=\"token keyword\">as</span> <span class=\"token class-name\">IChangeTrackable</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>SetChangedOnPublish <span class=\"token operator\">=</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n</span></code></pre>\n<p>Hmm, did someone say this isn't possible?\nHa, checkmate!</p>\n","summary":"The date can be changed by using Episerver's internal, undocumented API.\nLet's see how and also how I found the solution.","date_published":"2020-10-08T12:00:00+03:00","date_modified":"2021-10-28T12:00:00+03:00","tags":["C#","Episerver"]},{"id":"https://mtsknn.fi/blog/switch-statements-default-doesnt-have-to-be-the-last-case/","url":"https://mtsknn.fi/blog/switch-statements-default-doesnt-have-to-be-the-last-case/","title":"Switch statements: `default` doesn't have to be the last case","content_html":"<p>Non-last <code>default</code> cases are confusing\nbut perfectly valid.\nSometimes it makes sense to place them at the beginning or in the middle.</p>\n<h2 id=\"huh-thats-possible\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#huh-thats-possible\">Huh, that's possible?</a></h2>\n<p>If you think about all the <code>switch</code> statements you have ever seen in your life,\nI guess that in most of them,\nthe <code>default</code> case has been the last or almost last one.</p>\n<p>I still remember how dumbfounded I was\nwhen a friend of mine sent me\nthe following snippet of Unity code (C#)\nthat he had <del>copy-pasted</del> picked up from a course\nhe was following at the time:</p>\n<pre aria-label=\"Code block\" class=\"language-cs bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span aria-hidden=\"true\" class=\"absolute -ml-6 select-none w-full sm:-ml-5\"><br /><br /><br /><br /><span class=\"bg-gray-100 block border-gray-300 border-l-6 sm:border-l-4\"> </span></span><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token return-type class-name\"><span class=\"token keyword\">void</span></span> <span class=\"token function\">Update</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>state<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">default</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">case</span> State<span class=\"token punctuation\">.</span>WaitingToStart<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>Input<span class=\"token punctuation\">.</span><span class=\"token function\">GetKeyDown</span><span class=\"token punctuation\">(</span>KeyCode<span class=\"token punctuation\">.</span>Space<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> Input<span class=\"token punctuation\">.</span><span class=\"token function\">GetMouseButtonDown</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">{</span>\n                state <span class=\"token operator\">=</span> State<span class=\"token punctuation\">.</span>Playing<span class=\"token punctuation\">;</span>\n                rb<span class=\"token punctuation\">.</span>bodyType <span class=\"token operator\">=</span> RigidBodyType2D<span class=\"token punctuation\">.</span>Dynamic<span class=\"token punctuation\">;</span>\n                <span class=\"token function\">Jump</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> State<span class=\"token punctuation\">.</span>Playing<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>Input<span class=\"token punctuation\">.</span><span class=\"token function\">GetKeyDown</span><span class=\"token punctuation\">(</span>KeyCode<span class=\"token punctuation\">.</span>Space<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> Input<span class=\"token punctuation\">.</span><span class=\"token function\">GetMouseButtonDown</span><span class=\"token punctuation\">(</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">{</span>\n                <span class=\"token function\">Jump</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">case</span> State<span class=\"token punctuation\">.</span>Dead<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>As you can see,\nthe <code>default</code> case is at the very top.</p>\n<p>My initial thought was: dude, wtf, does this even compile?</p>\n<p>I was so used to seeing the <code>default</code> case at the bottom\nthat I couldn't comprehend this.\nIt looked like an <code>if</code> statement\nwhere the <code>else</code> branch is at the beginning.</p>\n<p>I think my inner logic circuit deduced that\nsince the <code>default</code> case is the first one,\nthe other cases are not even checked.</p>\n<p>But turns out that's not the case\n(no pun intended).\nThe code above is perfectly valid\nand even makes sense when you think about it.\n(We'll come back to this code snippet in\nthe <a href=\"#what-about-the-unity-code\" class=\"link link-anchor\">What about the Unity code?</a> section.)</p>\n<h2 id=\"the-position-of-the-default-case-doesnt-matter-unless-theres-a-fallthrough\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#the-position-of-the-default-case-doesnt-matter-unless-theres-a-fallthrough\">The position of the <code>default</code> case doesn't matter (unless there's a fallthrough)</a></h2>\n<p>After recovering from the shock of seeing that Unity code,\nI had to do some digging.</p>\n<p>Like the old saying goes,\n&quot;all programming-related Google searches lead to Stack Overflow.&quot;\nHere's\n<a href=\"https://stackoverflow.com/q/3110088/1079869\" title=\"Switch statement: must default be the last case?\" class=\"link link-external\">the highest-voted relevant SO question</a>\nthat I could find.\nLet's see what the accepted answer by Secure says:</p>\n<blockquote>\n<p>The C99 standard is not explicit about this, but taking all facts together, it is perfectly valid.</p>\n<p>A <code>case</code> and <code>default</code> label are equivalent to a <code>goto</code> label. [...]</p>\n<p>All cases are evaluated, then it jumps to the default label, if given.</p>\n</blockquote>\n<p>(The actual answer is more thorough with excerpts from the C99 standard.)</p>\n<p>Here's what's said about\n<a href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch\" class=\"link link-external\">JavaScript's <code>switch</code> statements on MDN</a>:</p>\n<blockquote>\n<p>A <code>switch</code> statement first evaluates its expression.\nIt then looks for the first <code>case</code> clause\nwhose expression evaluates to the same value as the result of the input expression\n[...]</p>\n<p>If no matching <code>case</code> clause is found,\nthe program looks for the optional <code>default</code> clause\n[...]\nBy convention, the <code>default</code> clause is the last clause, but it does not need to be so.</p>\n</blockquote>\n<p>One more:\nhere's what Paul Dixon has answered to\n<a href=\"https://stackoverflow.com/q/1241695/1079869\" title=\"default as first option in switch statement?\" class=\"link link-external\">a relevant SO question in the context of PHP</a>\nand o'boy do I agree:</p>\n<blockquote>\n<p>It is an unusual idiom,\nit causes a little pause when you're reading it,\na moment of &quot;huh?&quot;.\nIt works,\nbut most people would probably expect to find the default case at the end.</p>\n</blockquote>\n<p>Okay, you get the point:\nthe position of the <code>default</code> case doesn't matter in\nC, C#, JavaScript, PHP,\nand probably many other C-style languages as well –\n<em>if</em> there are no fallthroughs.</p>\n<p>If there are fallthroughs in a <code>switch</code> statement,\ni.e. there's no <code>break</code> or <code>return</code> statement between some cases,\nthe position of the <code>default</code> case might matter.\nThis is a common concept in <code>switch</code> statements,\nbut if it's still confusing how they work with non-last <code>default</code> cases,\nthe following code examples should clarify this.</p>\n<h2 id=\"situations-where-a-non-last-default-case-can-be-useful\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#situations-where-a-non-last-default-case-can-be-useful\">Situations where a non-last <code>default</code> case can be useful</a></h2>\n<p>While putting the <code>default</code> case to the end is usually the most intuitive option,\nI found four situations where a non-last <code>default</code> case can be useful.</p>\n<p>(If you can think of other useful situations,\nplease tell me!)</p>\n<h3 id=\"cases-in-logical-order\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#cases-in-logical-order\">Cases in logical order</a></h3>\n<p>Let's say you have cases from 1 to 3,\nand the default case is number 2.</p>\n<p>If you specify the cases so that the default case is the last one,\nthe order would be 1, 3 and 2/default.\nIt might instead be more intuitive to specify the cases in logical order:\n1, 2/default and 3.\nLike so:</p>\n<pre aria-label=\"Code block\" class=\"language-c bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>foo<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">case</span> <span class=\"token number\">1</span><span class=\"token operator\">:</span>\n    <span class=\"token comment\">// do something...</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">case</span> <span class=\"token number\">2</span><span class=\"token operator\">:</span>\n  <span class=\"token keyword\">default</span><span class=\"token operator\">:</span>\n    <span class=\"token comment\">// do something...</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">case</span> <span class=\"token number\">3</span><span class=\"token operator\">:</span>\n    <span class=\"token comment\">// do something...</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>The more cases there are and the longer they are,\nthe more sense this likely makes.\nOn the other hand,\nif the <code>default</code> case is in the middle\nof a long and complex <code>switch</code> statement,\nit might be easy to miss it.</p>\n<p>I got this idea from\n<a href=\"https://stackoverflow.com/a/3110163/1079869\" class=\"link link-external\">an SO answer by Salil</a>.</p>\n<h3 id=\"normal-case-first\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#normal-case-first\">Normal case first</a></h3>\n<p>Let's say you have a situation where there's\na normal/default case\nand a bunch of error cases.</p>\n<p>If you put the normal case to the end,\nit emphasizes the error cases.\nUnnecessarily so,\nespecially if the error cases are uncommon.</p>\n<p>I got this idea from\n<a href=\"https://stackoverflow.com/a/3110336/1079869\" class=\"link link-external\">an SO answer by kriss</a>\nwhich included this code example:</p>\n<pre aria-label=\"Code block\" class=\"language-c bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span><span class=\"token function\">poll</span><span class=\"token punctuation\">(</span>fds<span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">1000000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">default</span><span class=\"token operator\">:</span>\n    <span class=\"token comment\">// here goes the normal case : some events occured</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">case</span> <span class=\"token number\">0</span><span class=\"token operator\">:</span>\n    <span class=\"token comment\">// here goes the timeout case</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">case</span> <span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token operator\">:</span>\n     <span class=\"token comment\">// some error occurred, you have to check errno</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>kriss also talks about how the order of cases can affect performance,\nbut I'll just skip over this\nsince it smells like premature optimization.\n(<em>Maybe</em> it can be important\nif you are working with\nlow-level, performance-critical code\nand not thinking about this prematurely.)</p>\n<h3 id=\"flow-control-with-fallthroughs\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#flow-control-with-fallthroughs\">Flow control with fallthroughs</a></h3>\n<p>If you are using a <code>switch</code> statement for flow control\n(a series of steps)\nand don't break between cases,\nputting <code>default</code> to the beginning makes for a clear <code>switch</code> statement.</p>\n<p>The following code example is adapted from\n<a href=\"https://stackoverflow.com/a/19301402/1079869\" class=\"link link-external\">an SO answer by DanielM</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-c bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>step<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">default</span><span class=\"token operator\">:</span>\n  <span class=\"token keyword\">case</span> STEP_1<span class=\"token operator\">:</span>\n    <span class=\"token comment\">// do some stuff for step one</span>\n    <span class=\"token comment\">// fallthrough</span>\n  <span class=\"token keyword\">case</span> STEP_2<span class=\"token operator\">:</span>\n    <span class=\"token comment\">// this follows on from step 1 or you can skip straight to it</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>Another good example is\n&quot;a state machine where an invalid state should reset the machine and proceed as though it were the initial state,&quot;\nfrom <a href=\"https://stackoverflow.com/a/3111684/1079869\" class=\"link link-external\">an SO answer by supercat</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-c bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>widget_state<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">default</span><span class=\"token operator\">:</span> <span class=\"token comment\">/* Fell off the rails--reset and continue */</span>\n    widget_state <span class=\"token operator\">=</span> WIDGET_START<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">/* Fall through */</span>\n  <span class=\"token keyword\">case</span> WIDGET_START<span class=\"token operator\">:</span>\n    <span class=\"token comment\">// ...</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">case</span> WIDGET_WHATEVER<span class=\"token operator\">:</span>\n    <span class=\"token comment\">// ...</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>This is not limited to state machines.\nTake this example, adapted from\n<a href=\"https://stackoverflow.com/a/7198252/1079869\" class=\"link link-external\">an SO answer by Dan Larsen</a>:</p>\n<pre aria-label=\"Code block\" class=\"language-c bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">default</span><span class=\"token operator\">:</span>\n    <span class=\"token function\">printf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"No color selected so defaulting to \"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// Fallthrough</span>\n  <span class=\"token keyword\">case</span> COLOR_RED<span class=\"token operator\">:</span>\n    <span class=\"token function\">printf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"red\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">case</span> COLOR_BLUE<span class=\"token operator\">:</span>\n    <span class=\"token function\">printf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"blue\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">case</span> COLOR_GREEN<span class=\"token operator\">:</span>\n    <span class=\"token function\">printf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"green\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>If <code>color</code> is red, blue or green,\nthe code will print\njust the color.\nOtherwise\n(if <code>color</code> is e.g. yellow)\nthe code will print\n&quot;No color selected so defaulting to red.&quot;</p>\n<p>Though maybe it would be clearer\nto move the <code>default</code> case closer to the bottom\n(unless you want to\n<a href=\"#cases-in-logical-order\" class=\"link link-anchor\">sort the cases in logical order</a>):</p>\n<pre aria-label=\"Code block\" class=\"language-c bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>color<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">case</span> COLOR_BLUE<span class=\"token operator\">:</span>\n    <span class=\"token function\">printf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"blue\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">case</span> COLOR_GREEN<span class=\"token operator\">:</span>\n    <span class=\"token function\">printf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"green\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">default</span><span class=\"token operator\">:</span>\n    <span class=\"token function\">printf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"No color selected so defaulting to \"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// Fallthrough</span>\n  <span class=\"token keyword\">case</span> COLOR_RED<span class=\"token operator\">:</span>\n    <span class=\"token function\">printf</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"red\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<p>This is now similar to supercat's\n&quot;alternative arrangement [of the state machine where] an invalid state should not reset the machine\nbut should be readily identifiable as an invalid state&quot;:</p>\n<pre aria-label=\"Code block\" class=\"language-c bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token keyword\">switch</span> <span class=\"token punctuation\">(</span>widget_state<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">case</span> WIDGET_IDLE<span class=\"token operator\">:</span>\n    widget_ready <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">widget_hardware_off</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">case</span> WIDGET_START<span class=\"token operator\">:</span>\n    <span class=\"token comment\">// ...</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">case</span> WIDGET_WHATEVER<span class=\"token operator\">:</span>\n    <span class=\"token comment\">// ...</span>\n    <span class=\"token keyword\">break</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">default</span><span class=\"token operator\">:</span>\n    widget_state <span class=\"token operator\">=</span> WIDGET_INVALID_STATE<span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">/* Fall through */</span>\n  <span class=\"token keyword\">case</span> WIDGET_INVALID_STATE<span class=\"token operator\">:</span>\n    widget_ready <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n    <span class=\"token function\">widget_hardware_off</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token comment\">// ... do whatever else is necessary to establish a \"safe\" condition</span>\n<span class=\"token punctuation\">}</span>\n</span></code></pre>\n<h3 id=\"preventing-accidental-fallthroughs-to-the-default-case\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#preventing-accidental-fallthroughs-to-the-default-case\">Preventing accidental fallthroughs to the <code>default</code> case</a></h3>\n<p>If the <code>default</code> case is the first one,\nit's impossible for other cases\nto accidentally fall through to the <code>default</code> case.</p>\n<p>This idea is from\n<a href=\"https://stackoverflow.com/a/18470387/1079869\" class=\"link link-external\">an SO answer by JaredPar</a>.\nHe continues to clarify:\n&quot;This means <code>default</code> will run if, and only if,\nthe value matches no <code>case</code> statements in the <code>switch</code> block.&quot;</p>\n<p>Using a linter would also help you\ncatch accidental fallthroughs,\nso I don't know whether the confusion\ncaused by placing the <code>default</code> case to the top\noutweighs the benefits.\n(To be fair, JaredPar also writes:\n&quot;Note I'm not saying I agree with it,\nthis is simply the logic others have presented to me in the past.&quot;)</p>\n<h2 id=\"so-should-you-use-non-last-default-cases\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#so-should-you-use-non-last-default-cases\">So, should you use non-last <code>default</code> cases?</a></h2>\n<p>Unless you have a good reason to do otherwise,\nit's safest to put the <code>default</code> case to the end.\nThis way anyone familiar with <code>switch</code> statements\ncan more easily understand the structure of your <code>switch</code> statement.</p>\n<p>But if you have a good reason,\ngo ahead.\nJust be prepared that your code might be initially rather confusing to other developers.</p>\n<h2 id=\"what-about-the-unity-code\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#what-about-the-unity-code\">What about the Unity code?</a></h2>\n<p>I would put the mindboggling code sent to me by my friend to the\n<a href=\"#cases-in-logical-order\" class=\"link link-anchor\">Cases in logical order</a> category.\nIt also somewhat belongs to the\n<a href=\"#flow-control-with-fallthroughs\" class=\"link link-anchor\">Flow control with fallthroughs</a> category.</p>\n<p>After some thought,\nit's actually logical to present the game states (<code>switch</code> cases)\nin the order they have been presented:\n&quot;waiting to start,&quot; &quot;playing&quot; and &quot;dead.&quot;\nThat's the natural flow of the game states.\nThe initial game state is &quot;waiting to start,&quot;\nso the most logical position for the <code>default</code> case is at the beginning.</p>\n<p>In the end,\nthe code was actually quite clever!</p>\n","summary":"Non-last `default` cases are confusing\nbut perfectly valid.\nSometimes it makes sense to place them at the beginning or in the middle.","date_published":"2020-10-03T12:00:00+03:00","date_modified":"2021-03-10T12:00:00+03:00","tags":["C#","Clean code","JavaScript","TypeScript"]},{"id":"https://mtsknn.fi/blog/how-to-remember-markdowns-link-syntax/","url":"https://mtsknn.fi/blog/how-to-remember-markdowns-link-syntax/","title":"Mnemonic for Markdown's link syntax (and image syntax)","content_html":"<p>Was it <code>[text](url)</code> or <code>(text)[url]</code>?\nUse this one weird trick to remember –\nMarkdown consultants HATE it!\nSpoiler: it's the former since it looks like a function call.</p>\n<h2 id=\"link-syntax-looks-like-a-function-call\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#link-syntax-looks-like-a-function-call\">Link syntax looks like a function call</a></h2>\n<p>The first part looks like an array of words:\n<code>[link text]</code>.</p>\n<p>The second part looks like it's used to call a function with one parameter:\n<code>(url)</code>.</p>\n<p>Put together:</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token url\">[<span class=\"token content\">I'm an array of words</span>](<span class=\"token url\">/and-im-a-parameter-to-a-function-call</span>)</span>\n\n<span class=\"token url\">[<span class=\"token content\">Link text with <span class=\"token bold\"><span class=\"token punctuation\">**</span><span class=\"token content\">bold words</span><span class=\"token punctuation\">**</span></span></span>](<span class=\"token url\">https://example.com/</span>)</span>\n</span></code></pre>\n<h3 id=\"optional-second-parameter\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#optional-second-parameter\">Optional second parameter</a></h3>\n<p>Thinking of functions,\nyou can also provide a title as the second parameter.\nThese are all valid and identical\n(according to the <a href=\"https://spec.commonmark.org/0.30/\" class=\"link link-external\">CommonMark Spec</a>):</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token url\">[<span class=\"token content\">Link text</span>](<span class=\"token url\">/some-url</span> <span class=\"token string\">\"And a title\"</span>)</span>\n[Link text](/some-url 'And a title')\n[Link text](/some-url (And a title))\n</span></code></pre>\n<p>Output (×3):</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/some-url<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">title</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>And a title<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>Link text<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<p>The <code>title</code> attribute is not very accessible,\nso you shouldn't rely on it too much.</p>\n<h3 id=\"anti-mnemonic\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#anti-mnemonic\">Anti-mnemonic</a></h3>\n<p><code>(link text)[url]</code> would look like you are using the <code>url</code> key\nto access a value from... hmm, from the expression <code>(link text)</code>,\nwhich isn't even a valid expression?\nThat wouldn't make as much sense.</p>\n<h2 id=\"alternative-link-syntaxes\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#alternative-link-syntaxes\">Alternative link syntaxes</a></h2>\n<p>For completeness,\nlet's see other ways of specifying links in Markdown.\nReference links are also related to Markdown's image syntax.</p>\n<h3 id=\"autolinks\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#autolinks\">Autolinks</a></h3>\n<p>These are simple:\nwrap an absolute URI or an email address in angle brackets.</p>\n<p>Example:</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">&lt;https://example.com/foo/bar>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>mail@example.com</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<p>Output:</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>https://example.com/foo/bar<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>https://example.com/foo/bar<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>mailto:mail@example.com<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>mail@example.com<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<h3 id=\"reference-links\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#reference-links\">Reference links</a></h3>\n<p>A reference link consists of link text and a link label.</p>\n<p>The link label should have a corresponding &quot;link reference definition&quot; somewhere in the document,\nfor example at the end of a section or at the end of the whole document.\nLink labels are case-insensitive.</p>\n<p>Example:</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">Check out <span class=\"token url\">[<span class=\"token content\">foo</span>][<span class=\"token variable\">1</span>]</span>, <span class=\"token url\">[<span class=\"token content\">bar</span>][<span class=\"token variable\">2</span>]</span> and <span class=\"token url\">[<span class=\"token content\">baz</span>][<span class=\"token variable\">3</span>]</span>.\n\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> https://example.com/foo</span>\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">2</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> https://example.com/bar</span>\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">3</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> https://example.com/baz</span>\n\nDo you know what's <span class=\"token url\">[<span class=\"token content\">my favorite search engine</span>][<span class=\"token variable\">duck</span>]</span>?\nI like it more than <span class=\"token url\">[<span class=\"token content\">Microsoft's search engine</span>][<span class=\"token variable\">bing</span>]</span>.\n\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">duck</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> https://duck.com/</span>\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">bing</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> https://bing.com/</span>\n</span></code></pre>\n<p>Output:</p>\n<!-- prettier-ignore -->\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">Check out\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>https://example.com/foo<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>foo<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span>,\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>https://example.com/bar<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>bar<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span> and\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>https://example.com/baz<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>baz<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span>.\n\nDo you know what's <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>https://duck.com/<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>my favorite search engine<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span>?\nI like it more than <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>https://bing.com/<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>Microsoft's search engine<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span>.\n</span></code></pre>\n<h4>Collapsed reference links</h4>\n<p>If the link text and label are the same\n(labels are case-insensitive),\nyou can use &quot;collapsed reference links&quot; and drop the label:</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">Check out [foo][], [Bar][] and [BAZ &amp; qux][].\n\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">foo</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> https://example.com/foo</span>\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">bar</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> https://example.com/bar</span>\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">baz &amp; qux</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> https://example.com/baz</span>\n</span></code></pre>\n<h4>Shortcut reference links</h4>\n<p>&quot;Shortcut reference links&quot; take a step further by allowing you to drop the empty brackets as well:</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">Check out [foo], [Bar] and [BAZ &amp; qux].\n\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">foo</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> https://example.com/foo</span>\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">bar</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> https://example.com/bar</span>\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">baz &amp; qux</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> https://example.com/baz</span>\n</span></code></pre>\n<h2 id=\"image-syntax-link-syntax\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#image-syntax-link-syntax\">Image syntax = <code>!</code> + link syntax</a></h2>\n<p>The image syntax is the same as the link syntax,\nexcept that there's a bang (<code>!</code>) at the beginning,\nand the link text is the image's alt text:</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token url\"><span class=\"token operator\">!</span>[<span class=\"token content\">A nice pic</span>](<span class=\"token url\">/image-url</span>)</span>\n<span class=\"token url\"><span class=\"token operator\">!</span>[<span class=\"token content\">A nice pic</span>][<span class=\"token variable\">a nice pic</span>]</span>\n![A nice pic][]\n![A nice pic]\n\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">a nice pic</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> /image-url</span>\n</span></code></pre>\n<p>Output (×4):</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>img</span> <span class=\"token attr-name\">src</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/image-url<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">alt</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>A nice pic<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n</span></code></pre>\n<p>Omitting the alt text produces <code>alt=&quot;&quot;</code>:</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\">![](/image-url)\n![][a nice pic]\n\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">a nice pic</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> /image-url</span>\n</span></code></pre>\n<p>Output (×2):</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>img</span> <span class=\"token attr-name\">src</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/image-url<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">alt</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span><span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n</span></code></pre>\n<h2 id=\"image-link\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#image-link\">Image link</a></h2>\n<p>Nothing special; just the same link syntax where the link text is an image:</p>\n<pre aria-label=\"Code block\" class=\"language-md bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token url\">[<span class=\"token content\">![A nice pic</span>](<span class=\"token url\">/image-url</span>)</span>](/link-url)\n<span class=\"token url\">[<span class=\"token content\">![A nice pic</span>][<span class=\"token variable\">a nice pic</span>]</span>](/link-url)\n[![A nice pic][]](/link-url)\n[![A nice pic]](/link-url)\n\n<span class=\"token url\">[<span class=\"token content\">![A nice pic</span>](<span class=\"token url\">/image-url</span>)</span>][some page]\n<span class=\"token url\">[<span class=\"token content\">![A nice pic</span>][<span class=\"token variable\">a nice pic</span>]</span>][some page]\n[![A nice pic][]][some page]\n[![A nice pic]][some page]\n\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">a nice pic</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> /image-url</span>\n<span class=\"token url-reference url\"><span class=\"token punctuation\">[</span><span class=\"token variable\">some page</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span> /link-url</span>\n</span></code></pre>\n<p>Huh, quite busy syntax.</p>\n<p>Output (×8):</p>\n<pre aria-label=\"Code block\" class=\"language-html bg-white border-t border-b max-w-none -mx-6 my-4 relative sm:border sm:mx-0 sm:rounded-md\" role=\"region\" tabindex=\"0\"><code class=\"min-w-full !px-6 !py-4 relative sm:!px-5\"><span class=\"actual-code inline-block -ml-6 pl-6 relative sm:-ml-5 sm:pl-5\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>a</span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/link-url<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>img</span> <span class=\"token attr-name\">src</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/image-url<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">alt</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>A nice pic<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>a</span><span class=\"token punctuation\">></span></span>\n</span></code></pre>\n<h2 id=\"further-resources\" tabindex=\"-1\"><a class=\"link link-anchor\" href=\"#further-resources\">Further resources</a></h2>\n<p>The <a href=\"https://spec.commonmark.org/0.30/\" class=\"link link-external\">CommonMark Spec</a> has more examples and edge cases covered.\nCheck out these sections of the spec:</p>\n<ul>\n<li><a href=\"https://spec.commonmark.org/0.30/#links\" class=\"link link-external\">Links</a></li>\n<li><a href=\"https://spec.commonmark.org/0.30/#autolinks\" class=\"link link-external\">Autolinks</a></li>\n<li><a href=\"https://spec.commonmark.org/0.30/#reference-link\" class=\"link link-external\">Reference links</a></li>\n<li><a href=\"https://spec.commonmark.org/0.30/#images\" class=\"link link-external\">Images</a></li>\n</ul>\n","summary":"Was it `[text](url)` or `(text)[url]`?\nSpoiler: it's the former since it looks like a function call.","date_published":"2020-09-28T12:00:00+03:00","date_modified":"2022-04-25T12:00:00+03:00","tags":["Markdown","Mnemonics"]}]}