<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://43081j.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://43081j.com/" rel="alternate" type="text/html" /><updated>2026-03-21T17:44:53+00:00</updated><id>https://43081j.com/feed.xml</id><title type="html">James Garbutt</title><entry><title type="html">The Three Pillars of JavaScript Bloat</title><link href="https://43081j.com/2026/03/three-pillars-of-javascript-bloat" rel="alternate" type="text/html" title="The Three Pillars of JavaScript Bloat" /><published>2026-03-12T00:00:00+00:00</published><updated>2026-03-12T00:00:00+00:00</updated><id>https://43081j.com/2026/03/three-pillars-of-javascript-bloat</id><content type="html" xml:base="https://43081j.com/2026/03/three-pillars-of-javascript-bloat"><![CDATA[<p>Over the last couple of years, we’ve seen significant growth of the <a href="https://e18e.dev">e18e</a> community and a rise in performance focused contributions because of it. A large part of this is the “cleanup” initiative, where the community has been pruning packages which are redundant, outdated, or unmaintained.</p>

<p>One of the most common topics that comes up as part of this is “dependency bloat” - the idea that npm dependency trees are getting larger over time, often with long since redundant code which the platform now provides natively.</p>

<p>In this post, I want to briefly look at what I think are the three main types of bloat in our dependency trees, why they exist, and how we can start to address them.</p>

<h1 id="1-older-runtime-support-with-safety-and-realms">1. Older runtime support (with safety and realms)</h1>

<p><img src="/assets/images/is-string-graph.png" alt="is-string dependency graph" class="img-small" /></p>

<p>The graph above is a common sight in many npm dependency trees - a small utility function for something which seems like it should be natively available, followed by many similarly small deep dependencies.</p>

<p>So why is this a thing? Why do we need <code class="language-plaintext highlighter-rouge">is-string</code> instead of <code class="language-plaintext highlighter-rouge">typeof</code> checks? Why do we need <code class="language-plaintext highlighter-rouge">hasown</code> instead of <code class="language-plaintext highlighter-rouge">Object.hasOwn</code> (or <code class="language-plaintext highlighter-rouge">Object.prototype.hasOwnProperty</code>)? Three things:</p>

<ol>
  <li>Support for very old engines</li>
  <li>Protection against global namespace mutation</li>
  <li>Cross-realm values</li>
</ol>

<h2 id="support-for-very-old-engines">Support for very old engines</h2>

<p>Somewhere in the world, some people apparently exist who need to support <strong>ES3</strong> - think IE6/7, or extremely early versions of Node.js.<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">1</a></sup></p>

<p>For these people, much of what we take for granted today does not exist. For example, they don’t have any of the following:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Array.prototype.forEach</code></li>
  <li><code class="language-plaintext highlighter-rouge">Array.prototype.reduce</code></li>
  <li><code class="language-plaintext highlighter-rouge">Object.keys</code></li>
  <li><code class="language-plaintext highlighter-rouge">Object.defineProperty</code></li>
</ul>

<p>These are all ES5 features, meaning they simply don’t exist in ES3 engines.</p>

<p>For these unfortunate souls who are still running old engines, they need to reimplement everything themselves, or be provided with polyfills.</p>

<p>Alternatively, what’d be really nice is if they upgraded.</p>

<h2 id="protection-against-global-namespace-mutation">Protection against global namespace mutation</h2>

<p>The second reason for some of these packages is “safety”.</p>

<p>Basically, inside Node itself, there is a concept of “primordials”. These are essentially just global objects wrapped at startup and imported by Node from then on, to avoid Node itself being broken by someone mutating the global namespace.</p>

<p>For example, if Node itself uses <code class="language-plaintext highlighter-rouge">Map</code> and we re-define what <code class="language-plaintext highlighter-rouge">Map</code> is - we can break Node. To avoid this, Node keeps a reference to the original <code class="language-plaintext highlighter-rouge">Map</code> which it imports rather than accessing the global.</p>

<p>You can read more about this <a href="https://github.com/nodejs/node/blob/7547e795ef700e1808702fc2851a0dcc3395a065/doc/contributing/primordials.md">here in the Node repo</a>.</p>

<p>This makes a lot of sense <em>for an engine</em>, since it really shouldn’t fall over if a script messes up the global namespace.</p>

<p>Some maintainers also believe this is the correct way to build <em>packages</em>, too. This is why we have dependencies like <code class="language-plaintext highlighter-rouge">math-intrinsics</code> in the graph above, which basically re-exports the various <code class="language-plaintext highlighter-rouge">Math.*</code> functions to avoid mutation.</p>

<h2 id="cross-realm-values">Cross-realm values</h2>

<p>Lastly, we have cross-realm values. These are basically values you have passed from one realm to another - for example, from a web page to a child <code class="language-plaintext highlighter-rouge">&lt;iframe&gt;</code> or vice versa.</p>

<p>In this situation, a <code class="language-plaintext highlighter-rouge">new RegExp(pattern)</code> in an iframe, is <em>not</em> the same <code class="language-plaintext highlighter-rouge">RegExp</code> class as the one in the parent page. This means <code class="language-plaintext highlighter-rouge">window.RegExp !== iframeWindow.RegExp</code>, which of course means <code class="language-plaintext highlighter-rouge">val instanceof RegExp</code> would be <code class="language-plaintext highlighter-rouge">false</code> if it came from the iframe (another realm).</p>

<p>For example, I am a maintainer of chai, and we have this exact issue. We need to support assertions happening across realms (since a test runner may run tests in a VM or iframe), so we can’t rely on <code class="language-plaintext highlighter-rouge">instanceof</code> checks. For that reason, we use <code class="language-plaintext highlighter-rouge">Object.prototype.toString.call(val) === '[object RegExp]'</code> to check if something is a regex, which works across realms since it doesn’t rely on the constructor.</p>

<p>In the graph above, <code class="language-plaintext highlighter-rouge">is-string</code> is basically doing this same job in case we passed a <code class="language-plaintext highlighter-rouge">new String(val)</code> from one realm to another.</p>

<h2 id="why-this-is-a-problem">Why this is a problem</h2>

<p>All of this makes sense for a very small group of people. If you’re supporting very old engines, passing values across realms, or want protection from someone mutating the environment - these packages are exactly what you need.</p>

<p>The problem is that the vast majority of us don’t need any of this. We’re running a version of Node from the last 10 years, or using an evergreen browser. We don’t need to support pre-ES5 environments, we don’t pass values across frames, and we uninstall packages which break the environment.<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup></p>

<p>These layers of niche compatibility somehow made their way into the “hot path” of everyday packages. The tiny group of people who actually need this stuff should be the ones seeking out special packages for it. Instead, it is reversed and <strong>we all pay the cost</strong>.</p>

<h1 id="2-atomic-architecture">2. Atomic architecture</h1>

<p><a href="https://sindresorhus.com/blog/small-focused-modules">Some folks believe</a> that packages should be broken up to an almost atomic level, creating a collection of small building blocks which can later be re-used to build other higher level things.</p>

<p>This kind of architecture means we end up with graphs like this:</p>

<p><img src="/assets/images/execa-graph.png" alt="execa dependency graph" class="img-smaller" /></p>

<p>As you can see, the most granular snippets of code have their own packages. For example, <code class="language-plaintext highlighter-rouge">shebang-regex</code> is the following at the time of writing this post:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">shebangRegex</span> <span class="o">=</span> <span class="sr">/^#!</span><span class="se">(</span><span class="sr">.*</span><span class="se">)</span><span class="sr">/</span><span class="p">;</span>
<span class="k">export</span> <span class="k">default</span> <span class="nx">shebangRegex</span><span class="p">;</span>
</code></pre></div></div>

<p>By splitting code up to this atomic level, the theory is that we can then create higher level packages simply by joining the dots.</p>

<p>Some examples of these atomic packages to give you an idea of the granularity:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">arrify</code> - Converts a value to an array (<code class="language-plaintext highlighter-rouge">Array.isArray(val) ? val : [val]</code>)</li>
  <li><code class="language-plaintext highlighter-rouge">slash</code> - Replace backslashes in a file-system path with <code class="language-plaintext highlighter-rouge">/</code></li>
  <li><code class="language-plaintext highlighter-rouge">cli-boxes</code> - A JSON file containing the edges of a box</li>
  <li><code class="language-plaintext highlighter-rouge">path-key</code> - Get the <code class="language-plaintext highlighter-rouge">PATH</code> environment variable key for the current platform (<code class="language-plaintext highlighter-rouge">PATH</code> on Unix, <code class="language-plaintext highlighter-rouge">Path</code> on Windows)</li>
  <li><code class="language-plaintext highlighter-rouge">onetime</code> - Ensure a function is only called once</li>
  <li><code class="language-plaintext highlighter-rouge">is-wsl</code> - Check if <code class="language-plaintext highlighter-rouge">process.platform</code> is <code class="language-plaintext highlighter-rouge">linux</code> and <code class="language-plaintext highlighter-rouge">os.release()</code> contains <code class="language-plaintext highlighter-rouge">microsoft</code></li>
  <li><code class="language-plaintext highlighter-rouge">is-windows</code> - Check if <code class="language-plaintext highlighter-rouge">process.platform</code> is <code class="language-plaintext highlighter-rouge">win32</code></li>
</ul>

<p>If we wanted to build a new CLI for example, we could pull a few of these in and not worry about implementation. We don’t need to do <code class="language-plaintext highlighter-rouge">env['PATH'] || env['Path']</code> ourselves, we can just pull a package for that.</p>

<h2 id="why-this-is-a-problem-1">Why this is a problem</h2>

<p>In reality, most or all of these packages did not end up as the reusable building blocks they were meant to be. They’re either largely duplicated across various versions in a wider tree, or they’re single-use packages which only one other package uses.</p>

<h3 id="single-use-packages">Single use packages</h3>

<p>Let’s take a look at some of the most granular packages:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">shebang-regex</code> is used almost solely by <code class="language-plaintext highlighter-rouge">shebang-command</code> by the same maintainer</li>
  <li><code class="language-plaintext highlighter-rouge">cli-boxes</code> is used almost solely by <code class="language-plaintext highlighter-rouge">boxen</code> and <code class="language-plaintext highlighter-rouge">ink</code> by the same maintainer</li>
  <li><code class="language-plaintext highlighter-rouge">onetime</code> is used almost solely by <code class="language-plaintext highlighter-rouge">restore-cursor</code> by the same maintainer</li>
</ul>

<p>Each of these having only one consumer means they’re equivalent of inline code but cost us more to acquire (npm requests, tar extraction, bandwidth, etc.).</p>

<h3 id="duplication">Duplication</h3>

<p>Taking a look at <a href="https://npmgraph.js.org/?q=nuxt@4.4.2">nuxt’s dependency tree</a>, we can see a few of these building blocks duplicated:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">is-docker</code> (2 versions)</li>
  <li><code class="language-plaintext highlighter-rouge">is-stream</code> (2 versions)</li>
  <li><code class="language-plaintext highlighter-rouge">is-wsl</code> (2 versions)</li>
  <li><code class="language-plaintext highlighter-rouge">isexe</code> (2 versions)</li>
  <li><code class="language-plaintext highlighter-rouge">npm-run-path</code> (2 versions)</li>
  <li><code class="language-plaintext highlighter-rouge">path-key</code> (2 versions)</li>
  <li><code class="language-plaintext highlighter-rouge">path-scurry</code> (2 versions)</li>
</ul>

<p>Inlining them doesn’t mean we no longer duplicate the code, but it does mean we don’t pay the cost of things like version resolution, conflicts, cost of acquisition, etc.</p>

<p>Inlining makes duplication almost free, while packaging makes it expensive.</p>

<h3 id="larger-supply-chain-surface-area">Larger supply chain surface area</h3>

<p>The more packages we have, the larger our supply chain surface area is. Every package is a potential point of failure for maintenance, security, and so on.</p>

<p>For example, a maintainer of many of these packages was compromised last year. This meant hundreds of tiny building blocks were compromised, which meant the higher level packages we actually install were also compromised.</p>

<p>Logic as simple as <code class="language-plaintext highlighter-rouge">Array.isArray(val) ? val : [val]</code> probably doesn’t need its own package, security, maintenance, and so on. It can just be inlined and we can avoid the risk of it being compromised.</p>

<p>Similar to the first pillar, this philosophy made its way into the “hot path” and probably shouldn’t have. Again, we all pay the cost to no real benefit.</p>

<h1 id="3-ponyfills-that-overstayed-their-welcome">3. “Ponyfills” that overstayed their welcome</h1>

<p><img src="/assets/images/eslint-plugin-react-polyfills.svg" alt="eslint-plugin-react polyfills" class="img-small" /><sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">3</a></sup></p>

<p>If you’re building an app, you might want to use some “future” features your chosen engine doesn’t support yet. In this situation, a <strong>polyfill</strong> can come in handy - it provides a fallback implementation where the feature should be, so you can use it as if it were natively supported.</p>

<p>For example, <a href="https://npmx.dev/package/temporal-polyfill">temporal-polyfill</a> polyfills the new Temporal API so we can use <code class="language-plaintext highlighter-rouge">Temporal</code> regardless of if the engine supports it or not.</p>

<p>Now, if you’re building a library instead, what should you do?</p>

<p>In general, no library should load a polyfill as that is a consumer’s concern and a library shouldn’t be mutating the environment around it. As an alternative, some maintainers choose to use what’s called a <strong>ponyfill</strong> (sticking to the unicorns, sparkles and rainbows theme).</p>

<p>A ponyfill is basically a polyfill you import rather than one which mutates the environment.</p>

<p>This kinda works since it means a library can use future tech by importing an implementation of it which passes through to the native one if it exists, and uses the fallback otherwise. None of this mutates the environment, so it is safe for libraries to use.</p>

<p>For example, fastly provides <a href="https://github.com/fastly/performance-observer-polyfill/tree/455bd5eb62c1e07af3309e4c212f73c414e2a7d8?tab=readme-ov-file#usage-as-a-ponyfill">@fastly/performance-observer-polyfill</a>, which contains both a polyfill and ponyfill for <code class="language-plaintext highlighter-rouge">PerformanceObserver</code>.</p>

<h2 id="why-this-is-a-problem-2">Why this is a problem</h2>

<p>These ponyfills did their job at the time - they allowed the library author to use future tech without mutating the environment and without forcing the consumer to know which polyfills to install.</p>

<p>The problem comes when these ponyfills outstay their welcome. When the feature they fill in for is now supported by all engines we care about, the ponyfill should be removed. However, this often doesn’t happen and the ponyfill remains in place long after it’s needed.</p>

<p>We’re now left with many, many packages which rely on ponyfills for features we’ve all had for a decade now.</p>

<p>For example:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">globalthis</code> - ponyfill for <code class="language-plaintext highlighter-rouge">globalThis</code> (widely supported in 2019, 49M downloads a week)</li>
  <li><code class="language-plaintext highlighter-rouge">indexof</code> - ponyfill for <code class="language-plaintext highlighter-rouge">Array.prototype.indexOf</code> (widely supported in 2010, 2.3M downloads a week)</li>
  <li><code class="language-plaintext highlighter-rouge">object.entries</code> - ponyfill for <code class="language-plaintext highlighter-rouge">Object.entries</code> (widely supported in 2017, 35M downloads a week)</li>
</ul>

<p>Unless these packages are being kept alive because of <em>Pillar 1</em>, they’re usually still used just because nobody ever thought to remove them.</p>

<p>When all long-term support versions of engines have the feature, the ponyfill should be removed.<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">4</a></sup></p>

<h1 id="what-can-we-do-about-it">What can we do about it?</h1>

<p>Much of this bloat is so deeply nested in dependency trees today that it is a fairly hefty task to unravel it all and get to a good place. It will take time, and it will take a lot of effort from maintainers and consumers.</p>

<p>Having said that, I do think we can make significant progress on this front if we all work together.</p>

<p>Start asking yourself, “why do I have this package?” and “do I really need it?”.</p>

<p>If you find something which seems redundant, raise an issue with the maintainer asking if it can be removed.</p>

<p>If you encounter a direct dependency which has many of these issues, have a look for an alternative which doesn’t. A good start for that is the <a href="https://e18e.dev/docs/replacements/">module-replacements</a> project.</p>

<h2 id="using-knip-to-remove-unused-dependencies">Using knip to remove unused dependencies</h2>

<p><a href="https://knip.dev">knip</a> is a great project which can help you find and remove unused dependencies, dead code, and much more. In this case, it can be a great tool to help you find and remove dependencies you no longer use.</p>

<p>This doesn’t solve the problems above necessarily, but is a great starting point to help clean up the dependency tree before doing more involved work.</p>

<p>You can read more about how knip deals with unused dependencies in their <a href="https://knip.dev/typescript/unused-dependencies">documentation</a>.</p>

<h2 id="using-the-e18e-cli-to-detect-replaceable-dependencies">Using the e18e CLI to detect replaceable dependencies</h2>

<p>The <a href="https://github.com/e18e/cli">e18e CLI</a> has a super useful <code class="language-plaintext highlighter-rouge">analyze</code> mode to determine which dependencies are no longer needed, or have community recommended replacements.</p>

<p>For example, if you get something like this:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npx @e18e/cli analyze

...

│  Warnings:
│    • Module <span class="s2">"chalk"</span> can be replaced with native functionality. You can <span class="nb">read </span>more at
│      https://nodejs.org/docs/latest/api/util.html#utilstyletextformat-text-options. See more at
│      https://github.com/es-tooling/module-replacements/blob/main/docs/modules/chalk.md.

...
</code></pre></div></div>

<p>Using this, we can quickly identify which direct dependencies can be cleaned up. We can also then use the <code class="language-plaintext highlighter-rouge">migrate</code> command to automatically migrate some of these dependencies:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npx @e18e/cli migrate <span class="nt">--all</span>

e18e <span class="o">(</span>cli v0.0.1<span class="o">)</span>

┌  Migrating packages...
│
│  Targets: chalk
│
◆  /code/main.js <span class="o">(</span>1 migrated<span class="o">)</span>
│
└  Migration <span class="nb">complete</span> - 1 files migrated.
</code></pre></div></div>

<p>In this case, it will migrate from <code class="language-plaintext highlighter-rouge">chalk</code> to <code class="language-plaintext highlighter-rouge">picocolors</code>, a much smaller package which provides the same functionality.</p>

<p>In the future, this CLI will even recommend based on your environment - for example, it could suggest the native <a href="https://nodejs.org/docs/latest-v22.x/api/util.html#utilstyletextformat-text-options"><code class="language-plaintext highlighter-rouge">styleText</code></a> instead of a colours library if you’re running a new enough Node.</p>

<h2 id="using-npmgraph-to-investigate-your-dependency-tree">Using npmgraph to investigate your dependency tree</h2>

<p><a href="https://npmgraph.js.org">npmgraph</a> is a great tool to visualize your dependency tree and investigate where bloat is coming from.</p>

<p>For example, let’s take a look at the bottom half of <a href="https://npmgraph.js.org/?q=eslint@10.1.0">ESLint’s dependency graph</a> as of writing this post:</p>

<p><img src="/assets/images/eslint-graph.png" alt="eslint dependency graph" class="img-small" /></p>

<p>We can see in this graph that the <code class="language-plaintext highlighter-rouge">find-up</code> branch is isolated, in that nothing else uses its deep dependencies. For something as simple as an upwards file-system traversal, maybe we don’t need 6 packages. We can then go look for an alternative, such as <a href="https://npmx.dev/package/empathic"><code class="language-plaintext highlighter-rouge">empathic</code></a> which has a much smaller <a href="https://npmgraph.js.org/?q=empathic@2.0.0">dependency graph</a> and achieves the same thing.</p>

<h2 id="module-replacements">Module replacements</h2>

<p>The <a href="https://github.com/es-tooling/module-replacements">module replacements</a> project is being used as a central data set for the wider community to document which packages can be replaced with native functionality, or more performant alternatives.</p>

<p>If you’re ever in need of an alternative or just want to check your dependencies, this data set is great for that.</p>

<p>Similarly, if you come across packages in your tree which are made redundant by native functionality, or just have better battle-tested alternatives, this project is definitely a great place to contribute that so others can benefit from it.</p>

<p>Paired with the data, there’s also a <a href="https://github.com/es-tooling/module-replacements-codemods">codemods project</a> which provides codemods to automatically migrate some of these packages to their suggested replacements.</p>

<h1 id="closing-thoughts">Closing Thoughts</h1>

<p>We all pay the cost for an incredibly small group of people to have an unusual architecture they like, or a level of backwards compatibility they need.</p>

<p>This isn’t necessarily a fault of the people who made these packages, as each person should be able to build however they want. Many of them are an older generation of influential JavaScript developers - building packages in a darker time where many of the nice APIs and cross-compatibility we have today didn’t exist. They built the way they did because it was possibly the best way at the time.</p>

<p>The problem is that we never moved on from that. We still download all of this bloat today even though we’ve had these features for several years.</p>

<p>I think we can solve this by reversing things. This small group should pay the cost - they should have their own special stack pretty much only they use. Everyone else gets the modern, lightweight, and widely supported code.</p>

<p>Hopefully things like <a href="https://e18e.dev">e18e</a> and <a href="https://npmx.dev">npmx</a> can help with that through documentation, tooling, etc. You can also help by taking a closer look at your dependencies and asking “why?”. Raise issues with your dependencies asking them if, and why they need these packages anymore.</p>

<p>We can fix it.</p>

<h2 id="footnotes">Footnotes</h2>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:4" role="doc-endnote">
      <p>I believe there are people who need such old engines, but would love to see some examples <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Most of this bloat is from a time when it was probably necessary since the platform obviously wasn’t as feature-rich back then. I think it was probably the right decision/architecture at the time. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:1" role="doc-endnote">
      <p>Most mentioned years of support are from MDN, or if it pre-dates MDN, from the compat data <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>“Ponyfill” stuff in general is an unsettled topic, really. I think we should drop them once LTS is achieved, but others do disagree and want them “forever”. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><summary type="html"><![CDATA[A brief look at the three main causes of bloat in our JavaScript dependency trees, and how we can start to address them.]]></summary></entry><entry><title type="html">The bloat of edge-case first libraries</title><link href="https://43081j.com/2025/09/bloat-of-edge-case-libraries" rel="alternate" type="text/html" title="The bloat of edge-case first libraries" /><published>2025-09-09T00:00:00+00:00</published><updated>2025-09-09T00:00:00+00:00</updated><id>https://43081j.com/2025/09/bloat-of-edge-case-libraries</id><content type="html" xml:base="https://43081j.com/2025/09/bloat-of-edge-case-libraries"><![CDATA[<p>This is just some of what I’ve been pondering recently - particularly in terms of how we ended up with such overly-granular dependency trees.</p>

<p>I think we’ve ended up with many libraries in our ecosystem which are edge-case-first, the opposite to what I’d expect. I’ll give a few examples and some thoughts around this, mostly in the hope we can start to trim some of it away.</p>

<h1 id="the-problem">The problem</h1>

<p>I believe a lot of the questionably small libraries hiding in our deep dependency trees are a result of over-engineering for inputs and edge cases we’ve probably never seen.</p>

<p>For example, say we’re building a <code class="language-plaintext highlighter-rouge">clamp</code> function:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">function</span> <span class="nx">clamp</span><span class="p">(</span><span class="nx">value</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">min</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">max</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">number</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">min</span><span class="p">),</span> <span class="nx">max</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Pretty simple!</p>

<p>What if someone passes nonsensical ranges? Let’s handle that.</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">function</span> <span class="nx">clamp</span><span class="p">(</span><span class="nx">value</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">min</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">max</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">number</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">min</span> <span class="o">&gt;</span> <span class="nx">max</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">min must be less than or equal to max</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">min</span><span class="p">),</span> <span class="nx">max</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>This is probably as far as I’d go.</strong> But let’s over-engineer - what if someone passes a number-like string?</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">function</span> <span class="nx">clamp</span><span class="p">(</span><span class="nx">value</span><span class="p">:</span> <span class="kr">number</span> <span class="o">|</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">min</span><span class="p">:</span> <span class="kr">number</span> <span class="o">|</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">max</span><span class="p">:</span> <span class="kr">number</span> <span class="o">|</span> <span class="kr">string</span><span class="p">):</span> <span class="kr">number</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">value</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span> <span class="o">&amp;&amp;</span> <span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="nb">Number</span><span class="p">(</span><span class="nx">value</span><span class="p">)))</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">value must be a number or a number-like string</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">min</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span> <span class="o">&amp;&amp;</span> <span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="nb">Number</span><span class="p">(</span><span class="nx">min</span><span class="p">)))</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">min must be a number or a number-like string</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">max</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span> <span class="o">&amp;&amp;</span> <span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="nb">Number</span><span class="p">(</span><span class="nx">max</span><span class="p">)))</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">max must be a number or a number-like string</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span><span class="nb">Number</span><span class="p">(</span><span class="nx">min</span><span class="p">)</span> <span class="o">&gt;</span> <span class="nb">Number</span><span class="p">(</span><span class="nx">max</span><span class="p">))</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">min must be less than or equal to max</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">min</span><span class="p">),</span> <span class="nx">max</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>At this point, it seems clear to me we’ve just poorly designed our function. It solely exists to clamp numbers, so why would we accept strings?</p>

<p>But hey, let’s go further! What if other libraries also want to accept such loose inputs? Let’s extract this into a separate library:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">isNumber</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">is-number</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="kd">function</span> <span class="nx">clamp</span><span class="p">(</span><span class="nx">value</span><span class="p">:</span> <span class="kr">number</span> <span class="o">|</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">min</span><span class="p">:</span> <span class="kr">number</span> <span class="o">|</span> <span class="kr">string</span><span class="p">,</span> <span class="nx">max</span><span class="p">:</span> <span class="kr">number</span> <span class="o">|</span> <span class="kr">string</span><span class="p">):</span> <span class="kr">number</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isNumber</span><span class="p">(</span><span class="nx">value</span><span class="p">))</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">value must be a number or a number-like string</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isNumber</span><span class="p">(</span><span class="nx">min</span><span class="p">))</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">min must be a number or a number-like string</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isNumber</span><span class="p">(</span><span class="nx">max</span><span class="p">))</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">max must be a number or a number-like string</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span><span class="nb">Number</span><span class="p">(</span><span class="nx">min</span><span class="p">)</span> <span class="o">&gt;</span> <span class="nb">Number</span><span class="p">(</span><span class="nx">max</span><span class="p">))</span> <span class="p">{</span>
    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">min must be less than or equal to max</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">min</span><span class="p">),</span> <span class="nx">max</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Whoops!</strong> We’ve just created the infamous <a href="https://www.npmjs.com/package/is-number"><code class="language-plaintext highlighter-rouge">is-number</code></a> library!</p>

<h1 id="how-it-should-be">How it should be</h1>

<p>This, in my opinion, is poor technical design we’ve all ended up dealing with over the years. Carrying the baggage of these overly-granular libraries that exist to handle edge cases we’ve probably never encountered.</p>

<p>I think it should have been:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">function</span> <span class="nx">clamp</span><span class="p">(</span><span class="nx">value</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">min</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="nx">max</span><span class="p">:</span> <span class="kr">number</span><span class="p">):</span> <span class="kr">number</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">max</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="nx">min</span><span class="p">),</span> <span class="nx">max</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p><em>Maybe</em> with some <code class="language-plaintext highlighter-rouge">min &lt;= max</code> validation, but even that is debatable. At this point, you may as well inline the <code class="language-plaintext highlighter-rouge">Math.min(Math.max(...))</code> expression instead of using a dependency.</p>

<p><strong>We should be able to define our functions to accept the inputs they are designed for, and not try to handle every possible edge case.</strong></p>

<p>There are two things at play here:</p>

<ul>
  <li>Data types</li>
  <li>Values</li>
</ul>

<p>A well designed library would assume the right <strong>data types</strong> have been passed in, but may validate that the <strong>values</strong> make sense (e.g. <code class="language-plaintext highlighter-rouge">min</code> is less than or equal to <code class="language-plaintext highlighter-rouge">max</code>).</p>

<p>These over-engineered libraries have decided to implement <em>both</em> at runtime - essentially run-time type checking and value validation. One could argue that this is just a result of building in the pre-TypeScript era, but that still doesn’t justify the overly specific <em>value validation</em> (e.g. the real <code class="language-plaintext highlighter-rouge">is-number</code> also checks that it is finite).</p>

<h1 id="what-we-shouldnt-do">What we shouldn’t do</h1>

<p>We shouldn’t build edge-case-first libraries, i.e. those which solve for edge cases we have yet to encounter or are unlikely to ever encounter.</p>

<h2 id="example-is-arrayish-76m-downloadsweek">Example: <code class="language-plaintext highlighter-rouge">is-arrayish</code> (76M downloads/week)</h2>

<p>The <code class="language-plaintext highlighter-rouge">is-arrayish</code> library determines if a value is an <code class="language-plaintext highlighter-rouge">Array</code> or behaves like one.</p>

<p>There will be some edge cases where this matters a lot, where we want to accept something we can index into but don’t care if it is a real <code class="language-plaintext highlighter-rouge">Array</code> or not.</p>

<p>However, the common use case clearly will not be that and we could’ve just used <code class="language-plaintext highlighter-rouge">Array.isArray()</code> all along.</p>

<h2 id="example-is-number-90m-downloadsweek">Example: <code class="language-plaintext highlighter-rouge">is-number</code> (90M downloads/week)</h2>

<p>The <code class="language-plaintext highlighter-rouge">is-number</code> library determines if a value is a positive, finite number or number-like string (maybe we should name it <code class="language-plaintext highlighter-rouge">is-positive-finite-number</code> to be more accurate).</p>

<p>Again, there will be edge cases where we want to deal with number-like strings or we want to validate that a number is within a range (e.g. finite).</p>

<p>The common use case will not be this. The common use case will be that we want to check <code class="language-plaintext highlighter-rouge">typeof n === 'number'</code> and be done with it.</p>

<p>For those edge cases where we want to <em>additionally</em> validate what kind of number it is, we could use a library (but one which exists for the validation, not for the type check).</p>

<h2 id="example-pascalcase-97m-downloadsweek">Example: <code class="language-plaintext highlighter-rouge">pascalcase</code> (9.7M downloads/week)</h2>

<p>The <code class="language-plaintext highlighter-rouge">pascalcase</code> library transforms text to PascalCase.</p>

<p>It has 1 dependency (<code class="language-plaintext highlighter-rouge">camelcase</code>) and accepts a variety of input types:</p>

<ul>
  <li>strings</li>
  <li>null</li>
  <li>undefined</li>
  <li>arrays of strings</li>
  <li>functions</li>
  <li>arbitrary objects with <code class="language-plaintext highlighter-rouge">toString</code> methods</li>
</ul>

<p>In reality, almost every user will be passing a <code class="language-plaintext highlighter-rouge">string</code>.</p>

<h2 id="example-is-regexp-10m-downloadsweek">Example: <code class="language-plaintext highlighter-rouge">is-regexp</code> (10M downloads/week)</h2>

<p>The <code class="language-plaintext highlighter-rouge">is-regexp</code> library checks if a value is a <code class="language-plaintext highlighter-rouge">RegExp</code> object, and supports cross-realm values.</p>

<p>In reality, almost every user will be passing a <code class="language-plaintext highlighter-rouge">RegExp</code> object, and not one from another realm.</p>

<p>For context, cross-realm values can happen when you retrieve a value from an <code class="language-plaintext highlighter-rouge">iframe</code> or VM for example:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">iframe</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">iframe</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">iframe</span><span class="p">.</span><span class="nx">contentWindow</span><span class="p">.</span><span class="nb">RegExp</span> <span class="o">===</span> <span class="nb">RegExp</span><span class="p">;</span> <span class="c1">// false</span>

<span class="kd">const</span> <span class="nx">iframeRegex</span> <span class="o">=</span> <span class="nx">iframe</span><span class="p">.</span><span class="nx">contentWindow</span><span class="p">.</span><span class="nx">someRegexp</span><span class="p">;</span>

<span class="nx">iframeRegex</span> <span class="k">instanceof</span> <span class="nb">RegExp</span><span class="p">;</span> <span class="c1">// false</span>
<span class="nx">isRegex</span><span class="p">(</span><span class="nx">iframeRegex</span><span class="p">);</span> <span class="c1">// true</span>
</code></pre></div></div>

<p>This is indeed useful, and I do support this myself in chai (which I maintain). However, this is an edge case most libraries don’t need to care about.</p>

<h1 id="what-we-should-do">What we should do</h1>

<p>We should build libraries which solve the common use case and make assumptions about the input types they will be given.</p>

<h2 id="example-scule-18m-downloadsweek">Example: scule (1.8M downloads/week)</h2>

<p><a href="https://www.npmjs.com/package/scule">scule</a> is a library for transforming casing of text (e.g. camel case, etc).</p>

<p>It only accepts inputs it is designed for (strings and arrays of strings) and has zero dependencies.</p>

<p>In most of the functions it exports, it assumes valid input data types.</p>

<h2 id="example-dlv-149m-downloadsweek">Example: dlv (14.9M downloads/week)</h2>

<p><a href="https://www.npmjs.com/package/dlv">dlv</a> is a library for deep property access.</p>

<p>It only accepts strings and arrays of strings as the path to access, and assumes this (i.e. does no validation).</p>

<h1 id="validation-is-important">Validation is important</h1>

<p>Validation is important, and I want to be clear that I’m not saying we should stop validating our data.</p>

<p>However, we should usually be validating the data in the project that owns it (e.g. at the app level), and not in every library that later consumes it as input.</p>

<p>Deep dependencies applying validation like this actually shift the burden from where it belongs (at data boundaries) to deep in the dependency tree.</p>

<p>Often at this point, it is invisible to the consumer of the library.</p>

<p>How many people are passing values into <code class="language-plaintext highlighter-rouge">is-number</code> (via other libraries), not realising it will prevent them from using negative numbers and <code class="language-plaintext highlighter-rouge">Infinity</code>?</p>

<h1 id="a-note-on-overly-granular-libraries">A note on overly-granular libraries</h1>

<p>This post isn’t about overly-granular libraries in general, but I’d like to briefly mention them for visibility.</p>

<p>An overly-granular library is one where someone took a useful library and split it up into an almost atomic-level of granularity.</p>

<p>Some examples:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">shebang-regex</code> - 2LOC, does the same as <code class="language-plaintext highlighter-rouge">startsWith('#!')</code>, <strong>86M downloads/week</strong></li>
  <li><code class="language-plaintext highlighter-rouge">is-whitespace</code> - 7LOC, checks if a string is only whitespace, <strong>1M downloads/week</strong></li>
  <li><code class="language-plaintext highlighter-rouge">is-npm</code> - 8LOC, checks <code class="language-plaintext highlighter-rouge">npm_config_user_agent</code> or <code class="language-plaintext highlighter-rouge">npm_package_json</code> are set, <strong>7M downloads/week</strong></li>
</ul>

<p>This is a personal preference some maintainers clearly prefer. The thought seems to be that by having atomic libraries, you can easily build your next library mostly from the existing building blocks you have.</p>

<p>I don’t really agree with this and think downloading a package for <code class="language-plaintext highlighter-rouge">#!</code> 86 million times a week is a bit much.</p>

<h1 id="what-can-be-done-about-this">What can be done about this?</h1>

<p>The <a href="https://e18e.dev">e18e</a> community is already tackling a lot of this by contributing performance improvements across the ecosystem, including removing and replacing dependencies with more modern, performant ones.</p>

<p>Through these efforts, there’s already a useful <a href="https://e18e.dev/guide/replacements.html">list of replacements</a> and an <a href="https://github.com/es-tooling/eslint-plugin-depend/">ESLint plugin</a>.</p>

<h2 id="as-a-maintainer">As a maintainer</h2>

<p>If you’re maintaining a library, it would be worth reviewing your dependencies to see if:</p>

<ul>
  <li>Any are replaceable by native functionality these days (e.g. <code class="language-plaintext highlighter-rouge">Array.isArray</code>)</li>
  <li>Any are replaceable by smaller, less granular and/or more performant alternatives (e.g. <code class="language-plaintext highlighter-rouge">scule</code> instead of <code class="language-plaintext highlighter-rouge">pascalcase</code>)</li>
  <li>Any are redundant if you make more assumptions about input types</li>
</ul>

<p>Tools like <a href="https://npmgraph.js.org/">npmgraph</a> can help you visualise your dependency tree to make this task easier.</p>

<p>Also, being stricter around input types will allow you to reduce a lot of code and dependencies.</p>

<p>If you can assume the data being passed in is the correct type, you can leave validation up to the consumer.</p>

<h2 id="as-a-user">As a user</h2>

<p>Keep a close eye on your dependencies (both deep and direct), and what alternatives are available to your direct dependencies.</p>

<p>Often, it is easy to stick with a dependency from long ago and forget to re-visit it one day in case there is a better way. Many of these packages are possible natively, or have more modern alternatives.</p>

<p>Useful tools:</p>

<ul>
  <li><a href="https://npmgraph.js.org/">npmgraph</a> for visualising your dependency tree</li>
  <li><a href="https://node-modules.dev/">node-modules.dev</a> for visualising your dependencies and lots of useful meta data</li>
  <li><a href="https://docs.github.com/en/code-security/getting-started/dependabot-quickstart-guide">Dependabot</a> for keeping your dependencies up to date</li>
</ul>

<p>On the topic of data, it is also worth ensuring validation happens at data boundaries rather than being delegated to various dependencies. Try to validate the type and value up front, before passing into dependencies.</p>

<h1 id="conclusion">Conclusion</h1>

<p>Most of these libraries exist to handle edge cases that do certainly exist. However, <strong>we are all paying the cost of that rather than only those who need to support those edge cases</strong>.</p>

<p>This is the wrong way around. Libraries should implement the main use case, and alternatives (or plugins) can exist to provide the edge cases the minority needs.</p>

<p>We should all be more aware of what is in our dependency tree, and should push for more concise, lighter libraries.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[How building edge-case first led to bloated, overly-granular libraries and what we can do about it]]></summary></entry><entry><title type="html">Travels &amp;amp; open source</title><link href="https://43081j.com/2024/12/travels-and-open-source" rel="alternate" type="text/html" title="Travels &amp;amp; open source" /><published>2024-12-17T00:00:00+00:00</published><updated>2024-12-17T00:00:00+00:00</updated><id>https://43081j.com/2024/12/travels-and-open-source</id><content type="html" xml:base="https://43081j.com/2024/12/travels-and-open-source"><![CDATA[<p>I haven’t written on this blog in some time, and most of my posts here are very
technical (more like tutorials). However, I just returned from ~6 months of
travel and a lot has happened in that time that I figured it’d be fun to share.
So here goes!</p>

<h2 id="leaving-my-job">Leaving my job</h2>

<p>I had a pretty fun job as an engineering lead/principal with a really awesome
team alongside me. It gave me many opportunities to mentor people in the areas
I love, and introduce so many engineers to things I care about a lot (like web
components and web standards).</p>

<p>However, I’ve always loved travelling and had an itch in my brain for a long
time now, wanting to head in some random direction for a few months and see
more of the world.</p>

<p>In the end, I decided I’ve learnt what I can learn at my job, making now the
perfect time to finally go do that!</p>

<h2 id="travelling">Travelling</h2>

<p>I’m already pretty well travelled and have done countless solo trips over
many years. Combine that with the fact I’m happy without much of a plan, I
pretty much had to choose a direction and head that way.</p>

<p>I decided on a few things:</p>

<ul>
  <li>Aim to head East until I’m back home (i.e. go all the way around)</li>
  <li>Spend half of the trip exploring, half of it meeting up with friends I
haven’t seen in a while</li>
  <li>Start with a Europe trip, somehow reach Vietnam, and figure the rest out
from there</li>
</ul>

<p>It is worth noting that I did actually come back to the UK for a week
after the Europe portion of the trip (for arctangent festival!).</p>

<p>I planned most of my travel days only a day or two before, but I did have
a shortlist of cities I wanted to try reach for the second half of my trip
(since I wanted to meet up with specific people).</p>

<p>In the end, these are the countries I visited:</p>

<ul>
  <li>Belgium</li>
  <li>Germany</li>
  <li>Austria</li>
  <li>Hungary (where I finally got to hang out with
<a href="https://bsky.app/profile/patak.dev">@patak.dev</a>!)</li>
  <li>Czech Republic</li>
  <li>Poland</li>
  <li>Vietnam</li>
  <li>Japan</li>
  <li>Singapore</li>
  <li>Philippines</li>
  <li>Australia</li>
  <li>New Zealand</li>
  <li>USA (including good catch ups with
<a href="https://bsky.app/profile/marcushellberg.dev">@marcushellberg.dev</a> and
a few of the <a href="https://bsky.app/profile/lit.dev">@lit.dev</a> team)</li>
  <li>Canada</li>
</ul>

<p>From Canada, I also did a <em>small</em> detour to Germany to see some friends I made
in Vietnam and visit some Christmas markets. Great fun!</p>

<p>Overall, good trip. Made a lot of new friends, visited a few old friends, and
saw a lot of really awesome places.</p>

<h2 id="open-source">Open source</h2>

<p>One of the perks of having no job (for now) is that I have a bunch more
time to spend on my open source efforts!</p>

<p>This came at the ideal time, as we’d just launched the
<a href="https://e18e.dev">e18e</a> community. Being able to spend more time on that,
especially during the growing phase, has been super nice.</p>

<p>I’ve been able to work with so many like-minded people on all sorts of
performance improvements across the ecosystem. Not only cleanups, but
deep dives into CPU and memory profiles and much more.</p>

<p>We can’t forget, though, that I maintain and contribute to a whole bunch of
projects already. So I’ve basically been splitting my time between those
(e.g. chai, tinylibs, chokidar) and e18e.</p>

<p>This has been quite a lot of work but so much fun at the same time. Focusing
on the things I care about and providing a whole bunch of useful things
for the community.</p>

<h2 id="whats-next">What’s next?</h2>

<p>I’ll be focusing a lot on e18e still, trying to help grow the community and
have a larger effect on the performance of the tools and web apps we all use.</p>

<p>I have no idea what work I will end up in next, but I’ll always manage to cut
a chunk of time out for my OSS efforts.</p>

<p>Of course, I’ll probably also end up travelling again now that I’ve got
the bug!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[What I've been upto in the last 6 months]]></summary></entry><entry><title type="html">Using strongly typed events in TypeScript</title><link href="https://43081j.com/2020/11/typed-events-in-typescript" rel="alternate" type="text/html" title="Using strongly typed events in TypeScript" /><published>2020-11-07T00:00:00+00:00</published><updated>2020-11-07T00:00:00+00:00</updated><id>https://43081j.com/2020/11/typed-events-in-typescript</id><content type="html" xml:base="https://43081j.com/2020/11/typed-events-in-typescript"><![CDATA[<p>Recently I have been working on improving a whole bunch of web components
in one of our code bases. Part of this has been to improve the types and their
strength (which also means better editor support :D).</p>

<p>These are just a few tips from what I found.</p>

<h2 id="the-aim">The aim</h2>

<p>It is common that we might want to emit custom events from our components. For
example:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">HTMLElement</span> <span class="p">{</span>
  <span class="c1">// ..</span>
  <span class="k">protected</span> <span class="nx">_doSomething</span><span class="p">():</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">this</span><span class="p">.</span><span class="nx">dispatchEvent</span><span class="p">(</span><span class="k">new</span> <span class="nx">CustomEvent</span><span class="p">(</span><span class="dl">'</span><span class="s1">my-event</span><span class="dl">'</span><span class="p">));</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In these situations, a consumer of the component would listen like so:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">node</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">my-event</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">ev</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="c1">// ...</span>
<span class="p">});</span>
</code></pre></div></div>

<p>However, there would be no helpful type information to suggest that
<code class="language-plaintext highlighter-rouge">my-event</code> is a valid (“known”) event and the type of the event it is.</p>

<p>So our aim to strengthen this would be to strongly type those events and
give editors better completion/information.</p>

<h2 id="event-maps">Event maps</h2>

<p>Thankfully, TypeScript already helped solve this problem with “event maps”.
These are basically interfaces to hold the names of events and their type.</p>

<p>You can see that <code class="language-plaintext highlighter-rouge">addEventListener</code> is roughly typed like so:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// example map</span>
<span class="kr">interface</span> <span class="nx">EventMap</span> <span class="p">{</span>
  <span class="dl">'</span><span class="s1">my-event</span><span class="dl">'</span><span class="p">:</span> <span class="nx">MyCustomEventType</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// method</span>
<span class="nx">addEventListener</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="kr">keyof</span> <span class="nx">EventMap</span><span class="o">&gt;</span><span class="p">(</span>
  <span class="kd">type</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span>
  <span class="nx">listener</span><span class="p">:</span> <span class="p">(</span><span class="nx">event</span><span class="p">:</span> <span class="nx">EventMap</span><span class="p">[</span><span class="nx">T</span><span class="p">])</span> <span class="o">=&gt;</span> <span class="kr">any</span>
<span class="p">);</span>
</code></pre></div></div>

<p>Where <code class="language-plaintext highlighter-rouge">EventMap</code> is one of a few available maps depending on the type of node
you’re dealing with.</p>

<p>The fall back, for when it isn’t in the event map, is basically <code class="language-plaintext highlighter-rouge">Event</code>. This
means we’re not <em>too</em> strict.</p>

<p>Anyhow this results in useful hints and types like so:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">load</span><span class="dl">'</span><span class="p">,</span> <span class="nx">fn</span><span class="p">);</span> <span class="c1">// knows that 'load' is an event</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">blur</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">ev</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span> <span class="c1">// knows `ev` is `FocusEvent`</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">doesntexist</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">ev</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span> <span class="c1">// `ev` is `Event`</span>
</code></pre></div></div>

<h2 id="built-in-event-maps">Built in event maps</h2>

<p>There are several, a couple are:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">DocumentEventMap</code> - defines all events available to <code class="language-plaintext highlighter-rouge">Document</code></li>
  <li><code class="language-plaintext highlighter-rouge">WindowEventMap</code> - defines all events available to <code class="language-plaintext highlighter-rouge">Window</code></li>
  <li><code class="language-plaintext highlighter-rouge">HTMLElementEventMap</code> - defines all events available to any HTML element</li>
</ul>

<h2 id="extending-a-built-in-map">Extending a built in map</h2>

<p>You can extend a built in event map by augmenting the global interface:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">declare</span> <span class="nb">global</span> <span class="p">{</span>
  <span class="kr">interface</span> <span class="nx">WindowEventMap</span> <span class="p">{</span>
    <span class="dl">'</span><span class="s1">my-event</span><span class="dl">'</span><span class="p">:</span> <span class="nx">CustomEvent</span><span class="o">&lt;</span><span class="p">{</span><span class="na">foo</span><span class="p">:</span> <span class="kr">number</span><span class="p">}</span><span class="o">&gt;</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// ...</span>

<span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span>
  <span class="dl">'</span><span class="s1">my-event</span><span class="dl">'</span><span class="p">,</span> <span class="c1">// will be a known event name, suggested in your editor</span>
  <span class="p">(</span><span class="nx">ev</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// will be typed as `CustomEvent&lt;{foo: number}&gt;`</span>
    <span class="nx">ev</span><span class="p">.</span><span class="nx">detail</span><span class="p">.</span><span class="nx">foo</span><span class="p">;</span> <span class="c1">// number</span>
  <span class="p">}</span>
<span class="p">);</span>
</code></pre></div></div>

<h2 id="defining-your-own-event-maps">Defining your own event maps</h2>

<p>You may want to tie events to your component and only your component, rather
than the globally available list of events.</p>

<p>To do this, you can re-define <code class="language-plaintext highlighter-rouge">addEventListener</code>:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kr">interface</span> <span class="nx">MyEventMap</span> <span class="p">{</span>
  <span class="dl">'</span><span class="s1">my-event</span><span class="dl">'</span><span class="p">:</span> <span class="nx">CustomEvent</span><span class="o">&lt;</span><span class="p">{</span><span class="na">foo</span><span class="p">:</span> <span class="kr">number</span><span class="p">}</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">HTMLElement</span> <span class="p">{</span>
  <span class="k">public</span> <span class="nx">addEventListener</span><span class="o">&lt;</span><span class="nx">T</span> <span class="kd">extends</span> <span class="kr">keyof</span> <span class="nx">MyEventMap</span><span class="o">&gt;</span><span class="p">(</span>
    <span class="c1">// the event name, a key of MyEventMap</span>
    <span class="kd">type</span><span class="p">:</span> <span class="nx">T</span><span class="p">,</span>

    <span class="c1">// the listener, using a value of MyEventMap</span>
    <span class="nx">listener</span><span class="p">:</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">MyElement</span><span class="p">,</span> <span class="nx">ev</span><span class="p">:</span> <span class="nx">MyEventMap</span><span class="p">[</span><span class="nx">T</span><span class="p">])</span> <span class="o">=&gt;</span> <span class="kr">any</span><span class="p">,</span>

    <span class="c1">// any options</span>
    <span class="nx">options</span><span class="p">?:</span> <span class="nx">boolean</span> <span class="o">|</span> <span class="nx">AddEventListenerOptions</span>
  <span class="p">):</span> <span class="k">void</span><span class="p">;</span>

  <span class="c1">// the fallback for any event names not in our map</span>
  <span class="k">public</span> <span class="nx">addEventListener</span><span class="p">(</span>
    <span class="kd">type</span><span class="p">:</span> <span class="kr">string</span><span class="p">,</span>
    <span class="nx">listener</span><span class="p">:</span> <span class="p">(</span><span class="k">this</span><span class="p">:</span> <span class="nx">MyElement</span><span class="p">,</span> <span class="nx">ev</span><span class="p">:</span> <span class="nx">Event</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="kr">any</span><span class="p">,</span>
    <span class="nx">options</span><span class="p">?:</span> <span class="nx">boolean</span> <span class="o">|</span> <span class="nx">AddEventListenerOptions</span>
  <span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
    <span class="k">super</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="kd">type</span><span class="p">,</span> <span class="nx">listener</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// ...</span>

<span class="kd">const</span> <span class="nx">node</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">my-element</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span>
  <span class="dl">'</span><span class="s1">my-event</span><span class="dl">'</span><span class="p">,</span> <span class="c1">// strongly typed, suggested by editor</span>
  <span class="p">(</span><span class="nx">ev</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span> <span class="c1">// ev is a `CustomEvent&lt;{foo: number}&gt;`</span>
<span class="p">);</span>
</code></pre></div></div>

<p>It looks like there’s a lot going on here, but really we are just copying
what the original definition of <code class="language-plaintext highlighter-rouge">addEventListener</code> is from TypeScript’s
DOM definitions.</p>

<h2 id="wrap-up">Wrap up</h2>

<p>Again, this was just a quick one to show the findings. These things can
make the dev experience super nice, though.</p>

<p>Now in our editors we can get strongly typed event names along with their
strongly typed events, rather than relying on casts and what not.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[A quick few tips on how to use strongly typed events in TypeScript.]]></summary></entry><entry><title type="html">Using stylelint in a lit-element project</title><link href="https://43081j.com/2019/03/using-stylelint-with-lit" rel="alternate" type="text/html" title="Using stylelint in a lit-element project" /><published>2019-03-26T00:00:00+00:00</published><updated>2019-03-26T00:00:00+00:00</updated><id>https://43081j.com/2019/03/using-stylelint-with-lit</id><content type="html" xml:base="https://43081j.com/2019/03/using-stylelint-with-lit"><![CDATA[<div align="center">
  <img src="https://i.imgur.com/aAwxLFD.png" alt="Lint all the things" width="200" height="200" />
</div>

<h2 id="stylelint--lit-element--good-times">stylelint + lit-element = good times</h2>

<p>As some of you know, lately I’ve been very much involved in a few
<a href="https://github.com/43081j/eslint-plugin-wc">linting</a>
<a href="https://github.com/43081j/eslint-plugin-lit">projects</a>
as well as spending plenty of time contributing to the linters themselves.</p>

<p>One thing I’ve been missing so far in my own projects is the ability
to lint my CSS so I can easily enforce best practices and, more importantly,
detect potential problems in my rules/syntax.</p>

<p>This has been possible in projects which use CSS as-is for some time, using
the excellent open source <a href="https://github.com/stylelint/stylelint">stylelint</a>.</p>

<p>However, it has been a bit of a struggle when using a project like
<a href="https://github.com/Polymer/lit-element">lit-element</a> due to the fact that
most or all stylesheets are contained within template literals (for several
reasons I won’t go into here).</p>

<p>A PR or two recently got merged, though, so it is no longer such a struggle!</p>

<h2 id="a-lit-element">A lit element</h2>

<p>Here’s an example element which uses lit, so you get an idea of how
a stylesheet might look in this case:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">FooElement</span> <span class="kd">extends</span> <span class="nx">LitElement</span> <span class="p">{</span>
  <span class="k">static</span> <span class="kd">get</span> <span class="nx">styles</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">css</span><span class="s2">`
      :host { display: block; }
    `</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">html</span><span class="s2">`&lt;h1&gt;I'm some content!&lt;/h1&gt;`</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="the-problem--solution">The problem &amp; solution</h2>

<p>As mentioned before, the problem here is that the styles exist inside
a template literal rather than an actual stylesheet (i.e. a css file).</p>

<p>Due to this, stylelint can’t lint it out of the box as it has no idea
how to extract the CSS.</p>

<p>Here is where the very useful
<a href="https://github.com/styled-components/stylelint-processor-styled-components">stylelint-processor-styled-components</a>
processor comes in handy and can be <em>gently pushed</em> to into supporting our
needs.</p>

<p>This handy little processor allows stylelint to extract CSS from
<a href="https://github.com/styled-components/styled-components">styled-components</a>
which look a bit like this:</p>

<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">MyTitle</span> <span class="o">=</span> <span class="nx">styled</span><span class="p">.</span><span class="nx">h1</span><span class="s2">`
  color: hotpink;
`</span><span class="p">;</span>

<span class="c1">// This is rendered with hotpink color</span>
<span class="p">&lt;</span><span class="nc">MyTitle</span><span class="p">&gt;</span>Some Title<span class="p">&lt;/</span><span class="nc">MyTitle</span><span class="p">&gt;</span>
</code></pre></div></div>

<p>Looks familiar, right? If only we could tell the processor to look for
<code class="language-plaintext highlighter-rouge">css</code> templates rather than <code class="language-plaintext highlighter-rouge">styled</code> templates, right??</p>

<p>We’re in luck! This processor allows you to define which module
and which import name to extract CSS from with a bit of config:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="dl">"</span><span class="s2">importName</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">css</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">moduleName</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">lit-element</span><span class="dl">"</span><span class="p">,</span>
  <span class="dl">"</span><span class="s2">strict</span><span class="dl">"</span><span class="p">:</span> <span class="kc">true</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Each of these has the following meaning:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">importName</code> specifies which function to parse template strings for</li>
  <li><code class="language-plaintext highlighter-rouge">moduleName</code> specifies which module the function we specified must be
imported from (to avoid parsing non-lit <code class="language-plaintext highlighter-rouge">css</code> functions)</li>
  <li><code class="language-plaintext highlighter-rouge">strict</code> specifies that we only want to parse this specific import
from the module we specified</li>
</ul>

<p>The <code class="language-plaintext highlighter-rouge">strict</code> flag is a recent addition from me. I added it because
the processor’s default behaviour is to extract CSS from <em>all</em> lit-element
imports, which means it will try (and fail…) to lint <code class="language-plaintext highlighter-rouge">html</code> contents too.
This flag pretty much enables a little stricter parsing so only the named
import is parsed.</p>

<h2 id="setup">Setup</h2>

<p>So this’ll be fairly straight forward!</p>

<h3 id="install">Install</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm i -D stylelint
  stylelint-processor-styled-components
  stylelint-config-recommended
  stylelint-config-styled-components
</code></pre></div></div>

<h3 id="configure">Configure</h3>

<p>Create a <code class="language-plaintext highlighter-rouge">.stylelintrc.json</code>:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"processors"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">[</span><span class="w">
      </span><span class="s2">"stylelint-processor-styled-components"</span><span class="p">,</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"moduleName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"lit-element"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"importName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"css"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"strict"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"extends"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"stylelint-config-recommended"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"stylelint-config-styled-components"</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Simple. We enable the processor, then we tell it to only parse CSS
from lit-element’s <code class="language-plaintext highlighter-rouge">css</code> template function and we extend the provided
configs to do some bits of leg work for us.</p>

<h3 id="run-it">Run it!</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npx stylelint "src/**/*.js"
</code></pre></div></div>

<p>Add it to your <code class="language-plaintext highlighter-rouge">package.json</code> as a script, too, to make things a little
easier:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"lint:css"</span><span class="p">:</span><span class="w"> </span><span class="s2">"stylelint </span><span class="se">\"</span><span class="s2">src/**/*.js</span><span class="se">\"</span><span class="s2">"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h2 id="wrap-up">Wrap up</h2>

<p>I was stuck for quite some time having literally no clue how on earth
I lint some “pseudo-CSS” from inside some template expression. A few people
seemed to be in the same situation, so I hope this helped you out and
gave a good idea of the direction to head in.</p>

<p>Don’t forget to try out a code formatter too, such as
<a href="https://github.com/prettier/prettier">prettier</a>! You should totally make
use of something like this instead of relying on stylistic lint rules.</p>

<p>I had such crazily customised lint rules until I came to the revelation that
I just need to format code automatically instead of relying on <em>mere humans</em>.
Now our development process is much smoother, highly recommended.</p>

<p>Anyhow, enjoy becoming as much of a pain as I am with my error-level max
line length and JSDoc rules :x</p>]]></content><author><name></name></author><summary type="html"><![CDATA[A quick guide to using stylelint with a lit-element project]]></summary></entry><entry><title type="html">Using ESLint with TypeScript in 2019</title><link href="https://43081j.com/2019/02/using-eslint-with-typescript" rel="alternate" type="text/html" title="Using ESLint with TypeScript in 2019" /><published>2019-02-28T00:00:00+00:00</published><updated>2019-02-28T00:00:00+00:00</updated><id>https://43081j.com/2019/02/using-eslint-with-typescript</id><content type="html" xml:base="https://43081j.com/2019/02/using-eslint-with-typescript"><![CDATA[<p>For some time, the common solution to linting a TypeScript project has been
to use <a href="https://github.com/palantir/tslint">TSLint</a> and the standard for
linting JavaScript has quickly become <a href="https://eslint.org/">ESLint</a>.</p>

<p>Recently, though, TSLint
<a href="https://medium.com/palantir/tslint-in-2019-1a144c2317a9">announced</a> that
they were now going to work on the convergence of the two projects.</p>

<p>This is great news! However, some things should be cleared up and an example
of how we should now be linting our projects would be useful…</p>

<h2 id="eslint-already-supported-typescript-for-some-time">ESLint already supported TypeScript for some time</h2>

<p>ESLint has supported TypeScript for quite some time through its
<a href="https://github.com/typescript-eslint/typescript-eslint">typescript-eslint</a>
plugin and parser.</p>

<p>Quite a few people have asked me what they should do until TSLint “converges”
with ESLint. The answer is pretty much that <strong>they already converged</strong>,
long ago. TSLint’s blog post was a little unclear here, but do understand
you can already lint your TypeScript without TSLint.</p>

<h2 id="eslint-rules-can-still-be-used">ESLint rules can still be used</h2>

<p>TSLint wasn’t the best approach to linting TypeScript, in my opinion. The
correct solution was the one we’re now seeing: building support into the
linter everyone else already uses.</p>

<p>Due to TSLint’s standalone approach, it meant you couldn’t use any of your
usual ESLint rules and plugins but rather could only use their built in ones.</p>

<p>Now that you can use ESLint’s own TypeScript support, it means
<strong>you can use any other ESLint rules and plugins</strong> in addition to your
TypeScript rules.</p>

<h2 id="eslint-is-still-a-little-behind">ESLint is still a <em>little</em> behind</h2>

<p>Unfortunately, not all TypeScript-specific rules in ESLint make full use
of the TypeScript type system yet. Commonly, they operate on the JS
<em>after</em> transpilation has happened so some information can be lost.</p>

<p>Part of TSLint’s contributions (and plenty of other people already
contributing to this) will be to add type-system knowledge to the rules
so much stronger assertions can be built.</p>

<p>This’ll change/improve <strong>very soon</strong> as rules are already beginning to make use
of the type system.</p>

<h2 id="using-eslint-on-typescript-today">Using ESLint on TypeScript today</h2>

<p>Install:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm i -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
</code></pre></div></div>

<p>Configure <code class="language-plaintext highlighter-rouge">.eslintrc.json</code>:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"extends"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"eslint:recommended"</span><span class="p">,</span><span class="w">
    </span><span class="s2">"plugin:@typescript-eslint/recommended"</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"env"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"browser"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"parser"</span><span class="p">:</span><span class="w"> </span><span class="s2">"@typescript-eslint/parser"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"parserOptions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"ecmaVersion"</span><span class="p">:</span><span class="w"> </span><span class="mi">2017</span><span class="p">,</span><span class="w">
    </span><span class="nl">"sourceType"</span><span class="p">:</span><span class="w"> </span><span class="s2">"module"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"plugins"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="s2">"@typescript-eslint/eslint-plugin"</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"rules"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Run:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ eslint "src/**/*.ts"
</code></pre></div></div>

<p>Easy as that!</p>

<h2 id="recommendations">Recommendations</h2>

<h3 id="choose-compilation-options-over-lint-rules">Choose compilation options over lint rules</h3>

<p>You should usually choose compilation checks which the TypeScript
compiler handles rather than relying on the looser lint rules.</p>

<p>In particular, disable the <code class="language-plaintext highlighter-rouge">@typescript-eslint/no-unused-vars</code> rule and use
the following options in your <code class="language-plaintext highlighter-rouge">tsconfig.json</code> instead:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="dl">"</span><span class="s2">compilerOptions</span><span class="dl">"</span><span class="p">:</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">noUnusedLocals</span><span class="dl">"</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">noUnusedParameters</span><span class="dl">"</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">strict</span><span class="dl">"</span><span class="p">:</span> <span class="kc">true</span> <span class="c1">// This is useful too, to enable several strict checks</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="naming-conventions">Naming conventions</h3>

<p>Another useful one is member naming conventions:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"rules"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"@typescript-eslint/member-naming"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"error"</span><span class="p">,</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"private"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^__"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"protected"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^_"</span><span class="w">
    </span><span class="p">}]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This will enforce a convention many people already follow:</p>

<ul>
  <li>Private members begin with <code class="language-plaintext highlighter-rouge">__</code></li>
  <li>Protected members begin with <code class="language-plaintext highlighter-rouge">_</code></li>
</ul>

<p>With the introduction of private class fields coming soon, this may well
become redundant in future so use it as you feel best.</p>

<p>It is more of a personal preference.</p>

<h3 id="use-a-code-formatter-instead-of-stylistic-rules">Use a code formatter instead of stylistic rules</h3>

<p>I’ve tried having very strict lint rules for formatting, checking things
like indentation, spacing and trailing commas.</p>

<p>It always results in a painful development process unless you just happen
to have a personal preference for the exact same style. This is of course very
unlikely as we all have our own opinion.</p>

<p>A much better solution is to <strong>disable all formatting related lint rules</strong>
and instead use a formatter.</p>

<p>Two I use are:</p>

<ul>
  <li><a href="https://prettier.io/">prettier</a></li>
  <li><a href="https://clang.llvm.org/docs/ClangFormat.html">clang-format</a></li>
</ul>

<p>I’m gradually moving towards prettier, <em>I think</em>, but both generally do
a good job (and offer different styles so it is far from “which is best?”).</p>

<p>I use the following <code class="language-plaintext highlighter-rouge">.prettierrc</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
bracketSpacing: false
printWidth: 80
semi: true
singleQuote: true
tabWidth: 2
useTabs: false
arrowParens: always
</code></pre></div></div>

<p>I use the following <code class="language-plaintext highlighter-rouge">.clang-format</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BasedOnStyle: Google
AlignAfterOpenBracket: AlwaysBreak
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
BinPackArguments: false
</code></pre></div></div>

<p>Generally, ESLint will play nicely with both of these as long as you
remembered to turn off any
<a href="https://eslint.org/docs/rules/#stylistic-issues">stylistic rules</a>.</p>

<h3 id="use-a-editorconfig-file">Use a <code class="language-plaintext highlighter-rouge">.editorconfig</code> file</h3>

<p>One last thing which isn’t so much ESLint related is something I’d just
like to see people do more often.</p>

<p>Get into the habit of introducing a <a href="https://editorconfig.org"><code class="language-plaintext highlighter-rouge">.editorconfig</code></a>
file like so:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[*]
end_of_line = lf
indent_size = 2
indent_style = space
trim_trailing_whitespace = true
</code></pre></div></div>

<p>This makes your codebase <em>much</em> easier to edit by other people while
maintaing the same whitespace and indentation rules.</p>

<p>Almost all editors support this out of the box.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Linting TypeScript projects in 2019 and the way forward]]></summary></entry><entry><title type="html">Avoiding the TypeScript `any` type</title><link href="https://43081j.com/2019/02/typescript-avoid-using-any" rel="alternate" type="text/html" title="Avoiding the TypeScript `any` type" /><published>2019-02-01T00:00:00+00:00</published><updated>2019-02-01T00:00:00+00:00</updated><id>https://43081j.com/2019/02/typescript-avoid-using-any</id><content type="html" xml:base="https://43081j.com/2019/02/typescript-avoid-using-any"><![CDATA[<p>Lately, I’ve been spending a bit of time moving a few different projects
to use <code class="language-plaintext highlighter-rouge">strict: true</code> in the TypeScript compiler, as well as enabling
a <em>large</em> amount of lint rules.</p>

<p>One of these rules was <strong>no-any</strong>.</p>

<p>While this does mean everyone now hates me, it has also lead to people on
these projects learning and understanding the type system of TypeScript much
more than they previously did.</p>

<p>So here’s a few things we learnt by avoiding the <code class="language-plaintext highlighter-rouge">any</code> type…</p>

<h2 id="it-isnt-bad">It isn’t bad</h2>

<p>The <code class="language-plaintext highlighter-rouge">any</code> type isn’t necessarily a bad thing and, in actual fact, does still
come in useful sometimes. However, in most cases, there is a better alternative
that leads to having better defined types overall.</p>

<p>Anyhow, with that out of the way…</p>

<h2 id="unknown-can-usually-be-used-instead"><code class="language-plaintext highlighter-rouge">unknown</code> can usually be used instead</h2>

<p>The <a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type"><code class="language-plaintext highlighter-rouge">unknown</code></a>
type is relatively new, introduced in TypeScript 3.0.</p>

<p>It is similar to <code class="language-plaintext highlighter-rouge">any</code> in that it can be “anything”, but has one huge
difference: it can only be interacted with once we know the type (through
something like a type guard or inference).</p>

<p>A quick example:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">foo</span><span class="p">:</span> <span class="kr">any</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">foo</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">bar</span><span class="p">:</span> <span class="nx">unknown</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">bar</span><span class="dl">"</span><span class="p">;</span>

<span class="nx">foo</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="c1">// Works, type checking is effectively turned off for this</span>
<span class="nx">bar</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="c1">// Errors, bar is unknown</span>

<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">bar</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">bar</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="c1">// Works, we now know that bar is a string</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In most cases, you can do the ol’ switcheroo and <code class="language-plaintext highlighter-rouge">unknown</code> will work fine
where you once had <code class="language-plaintext highlighter-rouge">any</code>. Just be ready for a <del>painful</del> <em>fair</em> amount of
changes needed to add type guards or casts throughout your code…</p>

<h2 id="record-can-be-used-for-basic-objects"><code class="language-plaintext highlighter-rouge">Record</code> can be used for basic objects</h2>

<p>A common thing i’ve seen is the use of <code class="language-plaintext highlighter-rouge">any</code> in parsing API responses
(e.g. JSON) as the type of the objects may not always be known.</p>

<p>More than likely, such objects are… objects. So give <code class="language-plaintext highlighter-rouge">Record</code> a try
instead:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">foo</span><span class="p">:</span> <span class="kr">any</span> <span class="o">=</span> <span class="p">{</span> <span class="na">a</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">b</span><span class="p">:</span> <span class="mi">2</span> <span class="p">};</span>
<span class="kd">const</span> <span class="nx">bar</span><span class="p">:</span> <span class="nb">Record</span><span class="o">&lt;</span><span class="kr">string</span><span class="p">,</span> <span class="kr">number</span><span class="o">&gt;</span> <span class="o">=</span> <span class="p">{</span> <span class="na">a</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">b</span><span class="p">:</span> <span class="mi">2</span><span class="p">};</span>

<span class="nx">foo</span><span class="p">.</span><span class="nx">a</span><span class="p">;</span> <span class="c1">// Anything</span>
<span class="nx">bar</span><span class="p">.</span><span class="nx">a</span><span class="p">;</span> <span class="c1">// Number</span>

<span class="kd">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">response</span><span class="p">)</span> <span class="k">as</span> <span class="nb">Record</span><span class="o">&lt;</span><span class="kr">string</span><span class="p">,</span> <span class="nx">unknown</span><span class="o">&gt;</span><span class="p">[];</span>
<span class="nx">obj</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">id</span><span class="p">;</span> <span class="c1">// unknown but we can at least access it correctly</span>
</code></pre></div></div>

<p>As you can see, it can work well to combine this with <code class="language-plaintext highlighter-rouge">unknown</code> too if you
have more complex objects and you’ll at least have better than any.</p>

<h2 id="explicit-types-are-easier-to-understand-and-read">Explicit types are easier to understand and read</h2>

<p>This is more about types in general than just <code class="language-plaintext highlighter-rouge">any</code>, but we often had
cases like this:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">doSomething</span><span class="p">(</span><span class="nx">obj</span><span class="p">:</span> <span class="nx">Model</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">doAnotherThing</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">obj</span><span class="p">.</span><span class="nx">y</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">y</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This isn’t a great example, but it is already difficult from a glance
at the function to know what it returns.</p>

<p>If we instead had:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">doSomething</span><span class="p">(</span><span class="nx">obj</span><span class="p">:</span> <span class="nx">Model</span><span class="p">):</span> <span class="nx">XModel</span><span class="o">|</span><span class="nx">YModel</span><span class="o">|</span><span class="kc">null</span> <span class="p">{</span>
</code></pre></div></div>

<p>It is much clearer. This also applies to non-obvious variables and
anything else you leave for the compiler to infer, too.</p>

<p>It is true that editors will be able to show you this information either way,
but I think it is still nice to have explicit types so the code its self
is readable regardless of tooling.</p>

<h2 id="well-defined-types-are-lovely">Well-defined types are lovely</h2>

<p>You’ll soon realise how nice well-defined types are to work with. It is
most definitely worth the initial pain of forcing yourself to define
<em>everything</em>.</p>

<p>I often see objects in JavaScript and wonder, “alright, but what is it?”, when
I see it being dotted into. It is such a pleasant developer experience when
everything has a definition you can easily read through and understand.</p>

<p>This, combined with good editor support will leave a smile on your face:</p>

<p><img src="https://i.imgur.com/grGGejR.png" alt="Tab-completion" /></p>

<p>As you can see, without is <em>just terrible</em>.</p>

<h2 id="you-can-contribute-third-party-types">You can contribute third-party types</h2>

<p>Most of us have reached a point where we try to import a third-party
dependency and it has no types, so we’re either left with a nasty compile
error or we were naughty and turned the most lenient compile options on…</p>

<p>We’re all in need of good, strong types for popular dependencies. So
if you do find yourself in such a situation, please do write them and
contribute them to <a href="https://github.com/DefinitelyTyped/DefinitelyTyped">DefinitelyTyped</a>
or the project its self!</p>

<p>It will be of great help to all people who reach the same problems
you have in future.</p>

<h2 id="you-will-learn-more-advanced-types">You will learn more advanced types</h2>

<p>As you go deeper into the type system and start defining much more
complex objects, you’ll learn a lot of fun things.</p>

<p>Things you once thought were too dynamic or difficult to strongly
type will end up being easy for you.</p>

<p>A few good examples of what we can do are:</p>

<ul>
  <li><a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#conditional-types">Conditional types</a></li>
  <li><a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards">Type guard functions</a></li>
  <li><a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types">Mapped types</a></li>
</ul>

<h2 id="wrap-up">Wrap up</h2>

<p>So to wrap up:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"compilerOptions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"strict"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"noUnusedParameters"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"noUnusedLocals"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
    </span><span class="nl">"noImplicitReturns"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Then, if you’re ready for it, enable ESLint with the
<a href="https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-explicit-any.md">no-explicit-any</a>
rule at error level.</p>

<p>Do this, take the hit and you will thank yourself later :)</p>]]></content><author><name></name></author><summary type="html"><![CDATA[Reasons to avoid the `any` type and what to do instead]]></summary></entry><entry><title type="html">First thoughts on Deno, the JavaScript/TypeScript run-time</title><link href="https://43081j.com/2019/01/first-look-at-deno" rel="alternate" type="text/html" title="First thoughts on Deno, the JavaScript/TypeScript run-time" /><published>2019-01-27T00:00:00+00:00</published><updated>2019-01-27T00:00:00+00:00</updated><id>https://43081j.com/2019/01/first-look-at-deno</id><content type="html" xml:base="https://43081j.com/2019/01/first-look-at-deno"><![CDATA[<div style="text-align: center">
  <img src="https://deno.land/deno_logo.png" alt="Deno" width="150" height="134" />
</div>

<h2 id="deno-whats-that">Deno? What’s that?</h2>

<p>I found myself a little unproductive recently, so took a quick look at
<a href="https://github.com/trending">GitHub’s trending repos</a> page to see if there’s
anything cool and new to have a read of.</p>

<p>One of the top results was <a href="https://github.com/denoland/deno">Deno</a>. This
was interesting for a few reasons:</p>

<ul>
  <li>Written in <a href="https://www.rust-lang.org/">Rust</a> (this of course meant I had
to tell my good friend &amp; “rustacean”
<a href="https://twitter.com/willspeak">@willspeak</a> who loved the logo choice more
than anything)</li>
  <li>Natively supports both JavaScript and
<a href="https://www.typescriptlang.org/">TypeScript</a></li>
  <li><strong>Implements ES modules just like a browser</strong></li>
</ul>

<p>All of these points are of course <em>excellent</em>…</p>

<div style="text-align: center">
  <img src="https://i.imgur.com/JLOQVRZ.gif" alt="Excited GIF" />
</div>

<h2 id="why-would-i-need-it">Why would I need it?</h2>

<p><strong>It is easiest to think of deno as an alternative to NodeJS.</strong> It aims to
solve the same problem ultimately.</p>

<p>Node currently struggles a lot with playing nicely with newer APIs and
especially ES modules, though. This is where deno comes in, as it
aims to implement things the same as browsers do.</p>

<p>You and I would likely choose to use it if we wanted a <strong>single code base
that can be used in browsers and server-side as-is.</strong></p>

<h2 id="trying-it-out">Trying it out</h2>

<p>Deno is just another run-time for a language we already know, so your code
will pretty much be the same as it would in a browser (ignoring standard
library differences for now).</p>

<p>Here’s one of the examples from their docs:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">listen</span><span class="p">,</span> <span class="nx">copy</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">deno</span><span class="dl">"</span><span class="p">;</span>

<span class="p">(</span><span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">addr</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">0.0.0.0:8080</span><span class="dl">"</span><span class="p">;</span>
  <span class="kd">const</span> <span class="nx">listener</span> <span class="o">=</span> <span class="nx">listen</span><span class="p">(</span><span class="dl">"</span><span class="s2">tcp</span><span class="dl">"</span><span class="p">,</span> <span class="nx">addr</span><span class="p">);</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">listening on</span><span class="dl">"</span><span class="p">,</span> <span class="nx">addr</span><span class="p">);</span>
  <span class="k">while</span> <span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">conn</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">listener</span><span class="p">.</span><span class="nx">accept</span><span class="p">();</span>
    <span class="nx">copy</span><span class="p">(</span><span class="nx">conn</span><span class="p">,</span> <span class="nx">conn</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">})();</span>
</code></pre></div></div>

<p>Then to run it:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ deno foo.ts
deno requests network access to "listen". Grant? [yN] y
listening on 0.0.0.0:8080
</code></pre></div></div>

<p>You can also see here the security part of Deno (which I won’t be going into),
in that it has plenty of control over permissions of a script.</p>

<h2 id="thoughts">Thoughts</h2>

<p>A word of warning, most of these are going to be fairly technical points
which most people wouldn’t be so concerned about but do affect all of us.</p>

<p>I’ll be explaining these points with the assumption that you’ve gone
ahead and
<a href="https://github.com/denoland/deno/blob/master/Docs.md">tried deno out yourself</a>.</p>

<h3 id="basics">Basics</h3>

<p>If we’re not so concerned about the technical differences and differing
implementations of internals, both Node and deno behave <em>very</em> similarly.</p>

<p>Though, once it has matured, I think deno will be a good contender for us
to all move to.</p>

<p>Imagine writing all your projects, both libraries and apps, in one way and
having it work in both browsers and on the server just as-is. It would be
an amazing developer experience, and that is where deno is already at.</p>

<h3 id="modules">Modules</h3>

<p>One of the big problems with Node right now is that they’re in a difficult
position of trying to remain compatible with their own module system
(<code class="language-plaintext highlighter-rouge">require</code>) and the one defined in the spec (<code class="language-plaintext highlighter-rouge">import</code>).</p>

<p>Deno is great here, it doesn’t care about Node’s old off-spec module
system and only implements what is in the spec, ES Modules:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Deno &amp; Browsers</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">Foo</span><span class="p">,</span> <span class="nx">Bar</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./my-module.js</span><span class="dl">'</span><span class="p">;</span>

<span class="c1">// Node (CommonJS)</span>
<span class="kd">const</span> <span class="p">{</span><span class="nx">Foo</span><span class="p">,</span> <span class="nx">Bar</span><span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">./my-module.js</span><span class="dl">'</span><span class="p">);</span>
</code></pre></div></div>

<p>Modules are the greatest part of deno, for me, as it means we can have
sources which work in <strong>both browsers and deno</strong> without the need
for changes or a build process.</p>

<h3 id="package-locks-manifests-etc">Package locks, manifests, etc.</h3>

<p>A thing I think is <em>kinda</em> missing from deno is the concept of a manifest
and possibly also a lock file.</p>

<p><strong>Deno recommends checking your dependencies into source control</strong> so the
run-time can associate the imports with those files instead of trying
to retrieve them each time.</p>

<p>This does get around the need for a lock file, I suppose, but it feels a
little unconventional committing my dependencies to git…</p>

<p>Also, it seems like we’re dependending entirely on the dependency
URLs not changing in order to keep the same dependency graph between builds.
But what if a someone changes a git tag, or a branch, or the URL simply
vanishes? An uncached build would have plenty of trouble or a dependency
could unknowingly be changed.</p>

<p>As for a manifest, deno recommends creating a <code class="language-plaintext highlighter-rouge">package.ts</code> or some such
file:</p>

<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// package.ts</span>
<span class="k">export</span> <span class="p">{</span><span class="nx">Foo</span><span class="p">,</span> <span class="nx">Bar</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">https://foo.bar/branch/some-package.ts</span><span class="dl">'</span><span class="p">;</span>

<span class="c1">// mod.ts</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">Foo</span><span class="p">,</span> <span class="nx">Bar</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./package.ts</span><span class="dl">'</span><span class="p">;</span>
</code></pre></div></div>

<h3 id="the-standard-library">The standard library…</h3>

<p>Here’s where deno really becomes an obvious early-stages project which
needs a lot of TLC before it can be anything more than an experiment.</p>

<p>Just a few issues I have with it:</p>

<ul>
  <li>No clear process around what is included (who decides what modules
are introduced, or what they provide?)</li>
  <li>Some modules seem rushed (the datetime module is a good example,
<a href="https://github.com/denoland/deno_std/blob/4b054d69ad3e63e0a07d0df77a973b0ae5e0892d/datetime/mod.ts#L10">parsing via a bunch of conditionals</a>)</li>
  <li>Tests are lacking in some modules</li>
  <li>Not all modules follow the same structure, conventions, etc.</li>
</ul>

<p>I think this drills down to it being written by a very small group of
people without any well defined process in place. Hopefully, that will change
in future.</p>

<h3 id="compared-with-browsers">Compared with browsers</h3>

<p>Being that deno tries to simply implement the same specs that browsers do,
it should mean all code is portable (after TypeScript transpilation at
least).</p>

<p>This seems to be true, but there are still some minor issues around it.</p>

<p>For example, deno requires extensions on all imports but the TypeScript
language service (which editors like VSCode use) doesn’t currently
like this and complains.</p>

<p>A few other issues exist around this area from what I remember, but all
will be cleaned up/solved in time I am sure.</p>

<h2 id="wrap-up">Wrap up</h2>

<p>So to wrap up, I think deno is great and a major step forward. What Node
doesn’t dare do (a major breaking change for them), deno can and has done.</p>

<p>This is leading us to a much cleaner, much simpler solution which is aligned
perfectly with browsers. This is the way things should be.</p>

<p>I do hope it gets plenty of care, cleanup and so on. Eventually, I’d like
to see it at a point where we can all use it without any issues
and start enjoying having a single code base for all platforms.</p>

<p>Oh and of course, the logo is excellent.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[First thoughts on using the new JavaScript/TypeScript run-time]]></summary></entry><entry><title type="html">A Quick Note on Lit Types &amp;amp; Properties</title><link href="https://43081j.com/2018/10/lit-element-types" rel="alternate" type="text/html" title="A Quick Note on Lit Types &amp;amp; Properties" /><published>2018-10-10T00:00:00+00:00</published><updated>2018-10-10T00:00:00+00:00</updated><id>https://43081j.com/2018/10/lit-element-types</id><content type="html" xml:base="https://43081j.com/2018/10/lit-element-types"><![CDATA[<p>This is going to be a brief one, just to keep a note <em>somewhere</em> of how to
do these things and what they mean.</p>

<p>Lately, I’ve seen some confusion from the
<a href="https://www.polymer-project.org/">Polymer</a> community and consumers of
the new <a href="https://github.com/Polymer/lit-element">LitElement</a>…</p>

<p>The confusion? What are property types? How do we use properties? How
do things serialize and deserialize? How does this compare to Polymer?</p>

<p><strong>This post only applies if you pass values by attributes or reflect your
properties!</strong></p>

<p>Well here we are…</p>

<h2 id="polymer">Polymer</h2>

<p>I’m going to assume if you’re reading this, you’re aware of how Polymer
works and how to use the basics of it.</p>

<p>We define a property like so:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">PolymerElement</span> <span class="p">{</span>
  <span class="kd">static</span> <span class="kd">get</span> <span class="nx">properties</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span>
      <span class="na">prop1</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span>
      <span class="na">prop2</span><span class="p">:</span> <span class="nb">Boolean</span><span class="p">,</span>
      <span class="na">prop3</span><span class="p">:</span> <span class="nb">Object</span><span class="p">,</span>
      <span class="na">prop4</span><span class="p">:</span> <span class="nb">Array</span><span class="p">,</span>
      <span class="na">prop5</span><span class="p">:</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="nb">Array</span><span class="p">,</span> <span class="na">notify</span><span class="p">:</span> <span class="kc">true</span> <span class="p">}</span>
    <span class="p">};</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Polymer does some wizardry around this so pretty much all primitives and
built in types are parsed correctly.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;my-element</span> <span class="na">prop4=</span><span class="s">"[1,2,3]"</span><span class="nt">&gt;&lt;/my-element&gt;</span>
</code></pre></div></div>

<p>The above will result in <code class="language-plaintext highlighter-rouge">prop4</code> being the array, <code class="language-plaintext highlighter-rouge">[1, 2, 3]</code> after
Polymer deserializes it.</p>

<p>Even <code class="language-plaintext highlighter-rouge">Date</code> is parsed correctly and <code class="language-plaintext highlighter-rouge">Object</code> is parsed as JSON.</p>

<h2 id="litelement">LitElement</h2>

<p>Pretty much everyone who came from Polymer makes the mistake of trying the
same property types in Lit as they used in Polymer.</p>

<p>In Lit though, I suppose the idea is to remain lightweight, trimmed down and
lacking of any fancy logic like what Polymer had. This means <strong>most of these
types are not supported out of the box</strong>.</p>

<p>We define a property the same (which is why this assumption is an easy one to
make):</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">LitElement</span> <span class="p">{</span>
  <span class="kd">static</span> <span class="kd">get</span> <span class="nx">properties</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span>
      <span class="na">prop1</span><span class="p">:</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="nb">String</span> <span class="p">},</span>
      <span class="na">prop2</span><span class="p">:</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="nb">Boolean</span> <span class="p">},</span>
      <span class="na">prop3</span><span class="p">:</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="nb">Object</span> <span class="p">},</span>
      <span class="na">prop4</span><span class="p">:</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="nb">Array</span> <span class="p">}</span>
    <span class="p">};</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>This won’t work!</strong></p>

<p>Let’s take a look:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">&lt;my-element prop1="foo"&gt;</code> gives us <code class="language-plaintext highlighter-rouge">node.prop1 === "foo"</code></li>
  <li><code class="language-plaintext highlighter-rouge">&lt;my-element prop2="true"&gt;</code> gives us <code class="language-plaintext highlighter-rouge">node.prop2 === true</code></li>
  <li><code class="language-plaintext highlighter-rouge">&lt;my-element prop2="false"&gt;</code> gives us <strong><code class="language-plaintext highlighter-rouge">node.prop2 === true</code></strong></li>
  <li><code class="language-plaintext highlighter-rouge">&lt;my-element prop3='{ "foo": 5 }'&gt;</code> gives us
<strong><code class="language-plaintext highlighter-rouge">node.prop3 === '{ "foo" : 5 }'</code></strong></li>
  <li><code class="language-plaintext highlighter-rouge">&lt;my-element prop4="[1,2,3]"&gt;</code> gives us <strong><code class="language-plaintext highlighter-rouge">node.prop4 === "[1,2,3]"</code></strong></li>
</ul>

<p>You can see the last 3 are confusingly wrong. This is likely not what you
expected at all.</p>

<h2 id="how-it-works">How it works</h2>

<p><strong>A property type in Lit is a serializer object or function.</strong></p>

<p>When we <code class="language-plaintext highlighter-rouge">node.setAttribute('prop1', 'foo')</code> or <code class="language-plaintext highlighter-rouge">&lt;my-el prop1="foo"&gt;</code>, we
actually end up invoking <code class="language-plaintext highlighter-rouge">node.prop1 = String('foo')</code>.</p>

<p>Whatever we set our <code class="language-plaintext highlighter-rouge">type</code> to is used to deserialize the attribute value.</p>

<p>With <code class="language-plaintext highlighter-rouge">type: Foo</code>, we would result in <code class="language-plaintext highlighter-rouge">node.prop1 = Foo('foo')</code>.</p>

<p>It can be one of two possible types…</p>

<h3 id="a-function">A Function</h3>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">deserializer</span> <span class="o">=</span> <span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">str</span><span class="p">);</span>
</code></pre></div></div>

<p>In this case, given the element above but with:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
  <span class="nl">prop3</span><span class="p">:</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="nx">deserializer</span><span class="p">,</span> <span class="na">reflect</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span>
  <span class="nx">prop4</span><span class="p">:</span> <span class="p">{</span> <span class="nl">type</span><span class="p">:</span> <span class="nx">deserializer</span><span class="p">,</span> <span class="nx">reflect</span><span class="p">:</span> <span class="kc">true</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We would end up with:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;my-element</span> <span class="na">prop3=</span><span class="s">'{ "foo": 5 }'</span><span class="nt">&gt;&lt;/my-element&gt;</span>
<span class="c">&lt;!--
  node.prop3 would be the object { "foo" : 5 }
--&gt;</span>

<span class="nt">&lt;my-element</span> <span class="na">prop4=</span><span class="s">"[1,2,3]"</span><span class="nt">&gt;&lt;/my-element&gt;</span>
<span class="c">&lt;!--
  node.prop4 would be the array [1, 2, 3]
--&gt;</span>

<span class="c">&lt;!--
  Setting node.prop4 = [4, 5, 6] will reflect:
--&gt;</span>
<span class="nt">&lt;my-element</span> <span class="na">prop4=</span><span class="s">"4,5,6"</span><span class="nt">&gt;&lt;/my-element&gt;</span>
</code></pre></div></div>

<p>A function works when we only want special parsing but are happy with
<code class="language-plaintext highlighter-rouge">toString</code> when reflecting to the attribute.</p>

<p>You can see from the last example, we end up <em>serializing</em> into <code class="language-plaintext highlighter-rouge">4,5,6</code> because
that is what <code class="language-plaintext highlighter-rouge">[4,5,6].toString()</code> results in.</p>

<h3 id="an-object">An Object</h3>

<p>For cases where we want to handle serialization for reflecting values to
attributes, such as in the case of arrays, we need to provide an object:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">deserializer</span> <span class="o">=</span> <span class="p">{</span>
  <span class="nx">toAttribute</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">val</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="nx">fromAttribute</span><span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">str</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Now we see:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;my-element</span> <span class="na">prop4=</span><span class="s">"[1,2,3]"</span><span class="nt">&gt;&lt;/my-element&gt;</span>
<span class="c">&lt;!--
  node.prop4 would be the array [1, 2, 3]
--&gt;</span>

<span class="c">&lt;!--
  Setting node.prop4 = [4, 5, 6] will reflect:
--&gt;</span>
<span class="nt">&lt;my-element</span> <span class="na">prop4=</span><span class="s">"[4,5,6]"</span><span class="nt">&gt;&lt;/my-element&gt;</span>
</code></pre></div></div>

<p>The value is serialized correctly!</p>

<h3 id="built-ins">Built-ins</h3>

<p><code class="language-plaintext highlighter-rouge">Boolean</code>, <code class="language-plaintext highlighter-rouge">Number</code> and <code class="language-plaintext highlighter-rouge">String</code> do work out of the box, kind of.</p>

<p>Now that you know how it works:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">Boolean</span><span class="p">(</span><span class="dl">'</span><span class="s1">true</span><span class="dl">'</span><span class="p">)</span> <span class="o">===</span> <span class="kc">true</span>
<span class="nb">Boolean</span><span class="p">(</span><span class="dl">'</span><span class="s1">false</span><span class="dl">'</span><span class="p">)</span> <span class="o">===</span> <span class="kc">true</span>
<span class="nb">Number</span><span class="p">(</span><span class="dl">'</span><span class="s1">5</span><span class="dl">'</span><span class="p">)</span> <span class="o">===</span> <span class="mi">5</span>
<span class="nb">String</span><span class="p">(</span><span class="dl">'</span><span class="s1">foo</span><span class="dl">'</span><span class="p">)</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">foo</span><span class="dl">'</span>
</code></pre></div></div>

<p>You see, these constructors happen to behave the same way we want a
deserializer function to behave (though <code class="language-plaintext highlighter-rouge">Boolean</code> is <em>slightly</em> different).</p>

<p>Boolean is different because Lit handles <code class="language-plaintext highlighter-rouge">null</code> as <code class="language-plaintext highlighter-rouge">false</code>, for you.</p>

<h2 id="conclusion">Conclusion</h2>

<p>It is a bit confusing, yes.</p>

<p>It definitely means your <strong>Polymer properties are not compatible
with Lit</strong>, as you likely use <code class="language-plaintext highlighter-rouge">Object</code> and such a lot.</p>

<p>Lit may end up implementing these common serializers, though, seeing as pretty
much everyone would expect to use them. We shall see, as it would also
introduce more weight into Lit and that is what the team wants to avoid.</p>

<p>Until then, keep an eye on a
<a href="https://github.com/43081j/lit-element-serializers">little repo</a> I’m
playing around with as it aims to provide these common serializers
until there’s a better solution (or forever if there never is one).</p>

<p>Enjoy!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[A thought or two on using properties in Lit and types]]></summary></entry><entry><title type="html">LitElement with Rollup, Babel &amp;amp; Karma</title><link href="https://43081j.com/2018/09/polymer-lit-with-rollup" rel="alternate" type="text/html" title="LitElement with Rollup, Babel &amp;amp; Karma" /><published>2018-09-25T00:00:00+00:00</published><updated>2018-09-25T00:00:00+00:00</updated><id>https://43081j.com/2018/09/polymer-lit-with-rollup</id><content type="html" xml:base="https://43081j.com/2018/09/polymer-lit-with-rollup"><![CDATA[<p><a href="https://github.com/Polymer/lit-element">LitElement</a> is a cool new project
from the <a href="https://www.polymer-project.org/">Polymer</a> team. It serves as a great
base to start from when building
<a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components">web components</a>,
providing common necessities such as data binding, performant template
rendering and life-cycle methods.</p>

<p>As a regular contributor to many Polymer projects, I find myself playing
around with Lit quite a lot. Things appear to be fine, everything nicely
structured and everything works just as the team has defined…</p>

<p>However, in reality, a <em>lot</em> of people who discover Polymer and Lit will be
coming from backgrounds where they have used something completely different or
at least something with a very different development workflow.</p>

<p>One of those common workflows is the combination of
<a href="https://rollupjs.org/guide/en">Rollup</a>, <a href="https://babeljs.io/">Babel</a> and
<a href="https://karma-runner.github.io/2.0/index.html">Karma</a>.</p>

<p>So let’s take a look at how we can apply those while using LitElement…</p>

<h2 id="project-structure">Project Structure</h2>

<p>To begin with, I threw up a pretty basic project structure:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- src/
  - my-element.js
  - index.js
- package.json
- package-lock.json
- .editorconfig
</code></pre></div></div>

<p>The idea is that we will use Babel to transpile our sources into a <code class="language-plaintext highlighter-rouge">lib/</code>
directory and use Rollup to bundle those into a <code class="language-plaintext highlighter-rouge">bundle.js</code>.</p>

<h2 id="our-element">Our Element</h2>

<p>Our obvious dependencies are needed as a start:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm i -S @polymer/lit-element
</code></pre></div></div>

<p>The element being used for this setup is as follows:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// src/my-element.js</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">LitElement</span><span class="p">,</span> <span class="nx">html</span><span class="p">,</span> <span class="nx">property</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@polymer/lit-element</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">LitElement</span> <span class="p">{</span>
  <span class="p">@</span><span class="nd">property</span><span class="p">({</span><span class="na">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">})</span>
  <span class="nx">foo</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">test</span><span class="dl">'</span><span class="p">;</span>

  <span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nx">html</span><span class="s2">`&lt;h1&gt;</span><span class="p">${</span><span class="k">this</span><span class="p">.</span><span class="nx">foo</span><span class="p">}</span><span class="s2">&lt;/h1&gt;`</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="nx">customElements</span><span class="p">.</span><span class="nx">define</span><span class="p">(</span><span class="dl">'</span><span class="s1">my-element</span><span class="dl">'</span><span class="p">,</span> <span class="nx">MyElement</span><span class="p">);</span>

<span class="c1">// src/index.js</span>
<span class="k">export</span> <span class="p">{</span><span class="nx">MyElement</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./my-element</span><span class="dl">'</span><span class="p">;</span>
</code></pre></div></div>

<p>As you can see, it is fairly straight forward. More information on the
lifecycle methods and property setup can be found
<a href="https://github.com/Polymer/lit-element">here</a> and
<a href="https://43081j.com/2018/08/future-of-polymer">here</a>.</p>

<p>We’re just defining a very simple element which renders its only property,
<code class="language-plaintext highlighter-rouge">foo</code>.</p>

<h2 id="babel">Babel</h2>

<p>First of all, we need to install Babel along with any plugins and presets:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm i -D @babel/core @babel/cli @babel/preset-env
$ npm i -D @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators
</code></pre></div></div>

<p>The first three are pretty much to allow us to transpile from ES2015 and beyond
to a given level (in our case, to ES modules).</p>

<p>The two plugins are needed to allow us the use of Lit’s decorators as you
saw above:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">LitElement</span> <span class="p">{</span>
  <span class="p">@</span><span class="nd">property</span><span class="p">({</span><span class="na">type</span><span class="p">:</span> <span class="nb">String</span><span class="p">})</span>
  <span class="nx">foo</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">test</span><span class="dl">'</span><span class="p">;</span>
  <span class="cm">/* ... */</span>
<span class="p">}</span>
</code></pre></div></div>

<p>After installing these, a very simple
<a href="https://babeljs.io/docs/en/next/config-files">babelrc</a> will do:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"presets"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">[</span><span class="w">
      </span><span class="s2">"@babel/preset-env"</span><span class="p">,</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"modules"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
        </span><span class="nl">"targets"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"esmodules"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">],</span><span class="w">
  </span><span class="nl">"plugins"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">[</span><span class="w">
      </span><span class="s2">"@babel/plugin-proposal-decorators"</span><span class="p">,</span><span class="w">
      </span><span class="p">{</span><span class="w"> </span><span class="nl">"legacy"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="p">}</span><span class="w">
    </span><span class="p">],</span><span class="w">
    </span><span class="p">[</span><span class="w">
      </span><span class="s2">"@babel/plugin-proposal-class-properties"</span><span class="p">,</span><span class="w">
      </span><span class="p">{</span><span class="w"> </span><span class="nl">"loose"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>To try explain this a little, we:</p>

<ul>
  <li>Enable <a href="https://babeljs.io/docs/en/babel-preset-env">@babel/preset-env</a>
to turn on common Babel features</li>
  <li>Disable module generation (<code class="language-plaintext highlighter-rouge">"modules": false</code>) to prevent Babel
outputting CommonJS modules but rather have it output ES Modules</li>
  <li>Enable the ES modules target so only browsers which support it
are targeted</li>
  <li>Enable <em>legacy</em> decorators (until LitElement supports the newer
decorator proposal)</li>
  <li>Enable <em>loose</em> class properties (until we can stop using legacy decorators)</li>
</ul>

<p>This combination of things will result in us being able to use the
provided decorators and have our element properties defined as class
properties.</p>

<p>If we didn’t have any of this, our class would instead use the static
properties getter:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">class</span> <span class="nx">MyElement</span> <span class="kd">extends</span> <span class="nx">LitElement</span> <span class="p">{</span>
  <span class="kd">static</span> <span class="kd">get</span> <span class="nx">properties</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">{</span>
      <span class="na">foo</span><span class="p">:</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="nb">String</span> <span class="p">}</span>
    <span class="p">};</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now if we:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ node_modules/.bin/babel src -d lib
Successfully compiled 2 files with Babel.
</code></pre></div></div>

<p>We will have a populated <code class="language-plaintext highlighter-rouge">lib/</code> directory containing our transpiled
sources.</p>

<p>We should add this as a script in <code class="language-plaintext highlighter-rouge">package.json</code> to make things easier:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"build"</span><span class="p">:</span><span class="w"> </span><span class="s2">"babel src -d lib"</span><span class="p">,</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>So we can:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm run build
Successfully compiled 2 files with Babel.
</code></pre></div></div>

<h2 id="eslint-optional">ESLint (optional)</h2>

<p>It is fairly easy from this point to add ESLint to the workflow, though
not required at all:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm i -D eslint babel-eslint eslint-plugin-lit
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">babel-eslint</code> is an ESLint parser so we can have ESLint directly understand
our pre-transpiled sources.
<a href="https://github.com/43081j/eslint-plugin-lit"><code class="language-plaintext highlighter-rouge">eslint-plugin-lit</code></a>
introduces some handy lint rules specific to Lit.</p>

<p>I used a config like so:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"extends"</span><span class="p">:</span><span class="w"> </span><span class="s2">"eslint:recommended"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"parser"</span><span class="p">:</span><span class="w"> </span><span class="s2">"babel-eslint"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"rules"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"lit/no-legacy-template-syntax"</span><span class="p">:</span><span class="w"> </span><span class="s2">"warn"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"plugins"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"lit"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>You can add whichever rules you want from the Lit plugin, or none at all
if you wish.</p>

<p>Then you can simply introduce a new NPM script:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"build"</span><span class="p">:</span><span class="w"> </span><span class="s2">"babel src -d lib"</span><span class="p">,</span><span class="w">

  </span><span class="nl">"lint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"eslint </span><span class="se">\"</span><span class="s2">src/**/*.js</span><span class="se">\"</span><span class="s2">"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>To run:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm run lint
</code></pre></div></div>

<p>Simple!</p>

<h2 id="rollup">Rollup</h2>

<p>So far, we do have some sources a browser can understand. However, we do
not have support for Node (CommonJS) modules or the Node resolution algorithm.</p>

<p>This is where Rollup comes in:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// without Rollup or a loader/polyfill</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">LitElement</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./node_modules/@polymer/lit-element/lit-element.js</span><span class="dl">'</span><span class="p">;</span>

<span class="c1">// with Rollup</span>
<span class="k">import</span> <span class="p">{</span><span class="nx">LitElement</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@polymer/lit-element</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">someModule</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">a-node-module</span><span class="dl">'</span><span class="p">;</span>
</code></pre></div></div>

<p>If we use Rollup, we can use all our Node modules as normal and simply
have them bundled at build time.</p>

<p>Installing is pretty simple:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm i -D rollup rollup-plugin-commonjs rollup-plugin-node-resolve
</code></pre></div></div>

<p>The two plugins here give Rollup a helping hand with resolving Node
dependencies and understanding CommonJS modules (as opposed to the more
modern ES modules).</p>

<p>We can then throw up a config file as simple as:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">resolve</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">rollup-plugin-node-resolve</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">common</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">rollup-plugin-commonjs</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="na">input</span><span class="p">:</span> <span class="dl">'</span><span class="s1">lib/index.js</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">output</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">file</span><span class="p">:</span> <span class="dl">'</span><span class="s1">bundle.js</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">format</span><span class="p">:</span> <span class="dl">'</span><span class="s1">esm</span><span class="dl">'</span>
  <span class="p">},</span>
  <span class="na">plugins</span><span class="p">:</span> <span class="p">[</span>
    <span class="nx">common</span><span class="p">(),</span>
    <span class="nx">resolve</span><span class="p">()</span>
  <span class="p">]</span>
<span class="p">};</span>
</code></pre></div></div>

<p>This is as simple as it looks, it will use the two plugins mentioned earlier
and bundle our <code class="language-plaintext highlighter-rouge">lib/index.js</code> along with its dependencies into a <code class="language-plaintext highlighter-rouge">bundle.js</code>.</p>

<p>We can now introduce yet another NPM script:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"build"</span><span class="p">:</span><span class="w"> </span><span class="s2">"babel src -d lib"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"lint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"eslint </span><span class="se">\"</span><span class="s2">src/**/*.js</span><span class="se">\"</span><span class="s2">"</span><span class="p">,</span><span class="w">

  </span><span class="nl">"bundle"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run build &amp;&amp; rollup -c"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>You can see how we chained the Babel build so when we call:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm run bundle
</code></pre></div></div>

<p>We will actually transpile the sources, then bundle the output.</p>

<p>Now if we were to load our bundle in a browser:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script </span><span class="na">type=</span><span class="s">"module"</span> <span class="na">src=</span><span class="s">"bundle.js"</span><span class="nt">&gt;&lt;/script&gt;</span>
</code></pre></div></div>

<p>We would make <code class="language-plaintext highlighter-rouge">&lt;my-element&gt;</code> available just as expected.</p>

<h2 id="karma">Karma</h2>

<p>The last piece of the puzzle, which nobody should ever forget about, is testing!</p>

<p>You may be wondering why we don’t use something like Jest here. The answer to
that is very simple: Jest does not (yet?) support browser testing; instead it
uses JSDOM which does not yet support the web components APIs.</p>

<p>So, for now (or if you prefer Karma), we can use Karma instead:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm i -D karma karma-chrome-launcher karma-mocha chai
</code></pre></div></div>

<p>This is a pretty common bunch of dependencies: Karma with
<a href="https://mochajs.org/">Mocha</a> support and <a href="https://www.chaijs.com/">Chai</a>
assertions.</p>

<p>We can also install <a href="https://github.com/GoogleChrome/puppeteer">Puppeteer</a>
to automatically download an appropriate Chrome binary for us:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm i -D puppeteer
</code></pre></div></div>

<p>Our <code class="language-plaintext highlighter-rouge">karma.conf.js</code> is simple too:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Trick to use the auto-downloaded puppeteer chrome binary</span>
<span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">CHROME_BIN</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">puppeteer</span><span class="dl">'</span><span class="p">).</span><span class="nx">executablePath</span><span class="p">();</span>

<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">config</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">config</span><span class="p">.</span><span class="kd">set</span><span class="p">({</span>
    <span class="na">basePath</span><span class="p">:</span> <span class="dl">''</span><span class="p">,</span>
    <span class="na">frameworks</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">mocha</span><span class="dl">'</span><span class="p">],</span>
    <span class="na">files</span><span class="p">:</span> <span class="p">[</span>
      <span class="dl">'</span><span class="s1">bundle.test.js</span><span class="dl">'</span>
    <span class="p">],</span>
    <span class="na">reporters</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">progress</span><span class="dl">'</span><span class="p">],</span>
    <span class="na">autoWatch</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="na">browsers</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">ChromeHeadless</span><span class="dl">'</span><span class="p">],</span>
    <span class="na">singleRun</span><span class="p">:</span> <span class="kc">false</span>
  <span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We are going to create a second bundle specifically for tests
(<code class="language-plaintext highlighter-rouge">bundle.test.js</code>) and want to use headless Chrome.</p>

<p>To create this second bundle, firstly we need another rollup plugin:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm i -D rollup-plugin-multi-entry
</code></pre></div></div>

<p>The reason for this is that Rollup doesn’t provide multiple
entry point support by default, rather it expects a single entry point and
a single output (<code class="language-plaintext highlighter-rouge">src/index.js -&gt; lib/index.js</code>).</p>

<p>With this plugin, we can now create a <code class="language-plaintext highlighter-rouge">rollup.test.config.js</code>:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">resolve</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">rollup-plugin-node-resolve</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">common</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">rollup-plugin-commonjs</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="nx">multiEntry</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">rollup-plugin-multi-entry</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="na">input</span><span class="p">:</span> <span class="dl">'</span><span class="s1">lib/test/**/*.js</span><span class="dl">'</span><span class="p">,</span>
  <span class="na">output</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">file</span><span class="p">:</span> <span class="dl">'</span><span class="s1">bundle.test.js</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">format</span><span class="p">:</span> <span class="dl">'</span><span class="s1">esm</span><span class="dl">'</span>
  <span class="p">},</span>
  <span class="na">plugins</span><span class="p">:</span> <span class="p">[</span>
    <span class="nx">common</span><span class="p">({</span>
      <span class="na">namedExports</span><span class="p">:</span> <span class="p">{</span>
        <span class="dl">'</span><span class="s1">chai</span><span class="dl">'</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">expect</span><span class="dl">'</span><span class="p">]</span>
      <span class="p">}</span>
    <span class="p">}),</span>
    <span class="nx">resolve</span><span class="p">(),</span>
    <span class="nx">multiEntry</span><span class="p">()</span>
  <span class="p">]</span>
<span class="p">};</span>
</code></pre></div></div>

<p>This is all fairly simple… we now use a glob input (<code class="language-plaintext highlighter-rouge">lib/test/**/*.js</code>)
and have added <code class="language-plaintext highlighter-rouge">multiEntry()</code> as a plugin.</p>

<p>We have also specified <code class="language-plaintext highlighter-rouge">namedExports</code> because the Rollup plugin has trouble
automatically detecting what <code class="language-plaintext highlighter-rouge">chai</code> exports, unfortunately.</p>

<p>Another two NPM scripts are then added:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"build"</span><span class="p">:</span><span class="w"> </span><span class="s2">"babel src -d lib"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"lint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"eslint </span><span class="se">\"</span><span class="s2">src/**/*.js</span><span class="se">\"</span><span class="s2">"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"bundle"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run build &amp;&amp; rollup -c"</span><span class="p">,</span><span class="w">

  </span><span class="nl">"bundle:test"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run build &amp;&amp; rollup -c rollup.test.config.js"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"test"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run bundle:test &amp;&amp; karma start --singleRun"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>The first (<code class="language-plaintext highlighter-rouge">bundle:test</code>) simply runs our existing Babel build
and calls Rollup with our <code class="language-plaintext highlighter-rouge">rollup.test.config.js</code>.</p>

<p>The second (<code class="language-plaintext highlighter-rouge">test</code>) will run <code class="language-plaintext highlighter-rouge">bundle:test</code> and start the karma launcher
to execute our test suite.</p>

<h3 id="example-test">Example Test</h3>

<p>An example test would be <code class="language-plaintext highlighter-rouge">src/test/my-element.js</code>:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span><span class="nx">expect</span><span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">chai</span><span class="dl">'</span><span class="p">;</span>
<span class="c1">// Very important to ensure any elements we test are loaded</span>
<span class="k">import</span> <span class="dl">'</span><span class="s1">../my-element</span><span class="dl">'</span><span class="p">;</span>

<span class="nx">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">my-element</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">node</span><span class="p">;</span>

  <span class="nx">beforeEach</span><span class="p">(</span><span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="c1">// Use createElement to test it is registered correctly</span>
    <span class="nx">node</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">my-element</span><span class="dl">'</span><span class="p">);</span>

    <span class="c1">// Connect to DOM in case there's any `connectedCallback` logic</span>
    <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">node</span><span class="p">);</span>

    <span class="c1">// Wait for initial render</span>
    <span class="k">await</span> <span class="nx">node</span><span class="p">.</span><span class="nx">updateComplete</span><span class="p">;</span>
  <span class="p">});</span>

  <span class="nx">afterEach</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="c1">// Remove from DOM, cleanup</span>
    <span class="nx">node</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span>
  <span class="p">});</span>

  <span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">should render</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">span</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">shadowRoot</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">h1</span><span class="dl">'</span><span class="p">);</span>
    <span class="nx">expect</span><span class="p">(</span><span class="nx">span</span><span class="p">.</span><span class="nx">innerText</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="dl">'</span><span class="s1">test</span><span class="dl">'</span><span class="p">);</span>
  <span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>

<p>As you can see, this too is pretty straight forward. We didn’t need
any helpers, any fancy testing frameworks or custom loaders. We simply
bundle our sources as normal and execute the full test suite in Chrome.</p>

<h2 id="conclusion">Conclusion</h2>

<p>This is a very brief overview of how to set up this common work flow, so
I expect there’s plenty missing. I do hope it has helped somewhat, though.</p>

<p>While most of the Polymer team and its community are happy to push the idea
of everything being native and working as-is in the browser, it is not always
reality.</p>

<p>Maybe you’re required to use this workflow to remain consistent
with your other projects, or maybe you just like being able to use Node
modules. Either way, this should at least give you an idea of how to stick
to that process while also adopting something as new as Lit.</p>

<p>Now whether you use bundlers or not, you can try Lit out. Have
a <a href="https://codepen.io/sorvell/pen/RYQyoe?editors=1000">play around</a>!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[A brief example of how to set up LitElement with Rollup, Babel & Karma]]></summary></entry></feed>