<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Marvin Hagemeister</title>
  <subtitle>Musings about Preact, virtual DOM and all things related to JavaScript</subtitle>
  <link href="" rel="self"/>
  <link href="https://marvinh.dev/"/>
  <updated>2026-01-04T00:00:00Z</updated>
  <id>https://marvinh.dev/</id>
  <author>
    <name>Marvin Hagemeister</name>
    <email>hello@marvinh.dev</email>
  </author>
  
  <entry>
    <title>Signals vs Query-Based Compilers</title>
    <link href="https://marvinh.dev/blog/signals-vs-query-based-compilers/"/>
    <updated>2026-01-04T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/signals-vs-query-based-compilers/</id>
    <content type="html">&lt;p&gt;Over the winter holidays curiosity got the better of me and I spent a lot of time reading up on how modern compilers achieve interactivity in the days of LSPs and tight editor integrations. And it turns out that modern compilers are built around the same concept as Signals in UI rendering, with some interesting different design choices.&lt;/p&gt;
&lt;h2&gt;Old Days: The Pipeline Architecture&lt;/h2&gt;
&lt;p&gt;The classic teachings about compilers present them as a linear sequence of phases that code runs through until you end up with the final binary. Writing a compiler this way is pretty straight forward, assuming that the language is reasonably simple (JavaScript is ridiculously complex and the opposite of simple).&lt;/p&gt;
&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;Source Text -&gt; AST -&gt; IR -&gt; Assembly -&gt; Linker -&gt; Binary&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, source code is transformed into an Abstract Syntax Tree (=AST) which transforms the input text into structured objects/structs. It&#39;s the place where syntax errors, grammar errors and those kinds of things are caught.&lt;/p&gt;
&lt;p&gt;For example: This JavaScript source code...&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...is transformed into something similar to this AST:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;VariableDeclaration&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;kind&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;const&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;declarations&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;VariableDeclarator&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token property&quot;&gt;&quot;init&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NumericLiteral&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token property&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The AST is then pushed through a few more steps until we end up with the final binary.&lt;/p&gt;
&lt;p&gt;The actual details of these steps are not important for this blog post and what we described here is very oversimplified. But it&#39;s important to know that compilers typically run code through a bunch of different stages until code can be executed. Doing all of this takes time. Too much time to run on every key stroke.&lt;/p&gt;
&lt;h2&gt;Modern: Query Based Compilers&lt;/h2&gt;
&lt;p&gt;When a developer changes a single file by typing a single letter, there is a lot of work going on behind the scenes. Ideally, you want to do as little work as possible. Sure, you could add caches to every stage and try to come up with a good heuristic of when to invalidate them, but it becomes difficult to maintain pretty quickly. That&#39;s not good.&lt;/p&gt;
&lt;p&gt;The key shift in compilers is to not think of them as just a pipeline of transformations, but as a thing you can run queries on. When a user is typing in their editor the LSP asks the editor what are the suggestions at this specific cursor position in this file? When you click &amp;quot;Go to Definition&amp;quot; on an identifier you&#39;re asking the compiler to return the jump target (if any).&lt;/p&gt;
&lt;p&gt;Essentially, questions are a bunch of queries that you run against your compiler and the compiler should only focus on answering these as quickly as possible and ignore the rest.&lt;/p&gt;
&lt;p&gt;This mental shift is what makes modern compilers much more interactive. But how do they do that under the hood and what has this to do with signals?&lt;/p&gt;
&lt;h3&gt;Queries, Inputs and the Database&lt;/h3&gt;
&lt;p&gt;Query-based compilers have three key building blocks: Queries, Inputs and a &amp;quot;Database&amp;quot;. The core idea is that everything, literally everything, is composed out of queries and inputs. Nothing runs out of the box unless a query is executed.&lt;/p&gt;
&lt;p&gt;There is one big query at the top that says &amp;quot;Give me the final binary&amp;quot;, this query then fires off multiple other queries like &amp;quot;Give me the IR&amp;quot;, &amp;quot;Give me the AST&amp;quot;, etc. It&#39;s all queries all the way down.&lt;/p&gt;
&lt;p&gt;But there are also additional queries like: &amp;quot;What&#39;s the type of the identifier in file X at cursor position Y&amp;quot;. Such a query fires then another query that triggers the current file to be parsed into AST. With that you can fire another query that gets you the identifier at the cursor position. With that we can then fire off another query that resolves the identifier to its definition. If that turns out to be in another, we ask for that file to be parsed, etc.&lt;/p&gt;
&lt;p&gt;The beauty of this architecture is that you don&#39;t spend time processing completely unrelated files. You only process what is necessary to respond to a particular query. If a source file is completely unrelated to the running query it will never be processed.&lt;/p&gt;
&lt;h3&gt;Caches, caches and more caches&lt;/h3&gt;
&lt;p&gt;To speed things up even further, queries can easily be cached because they are expected to be pure. They should not have any side effects. This means that you can always re-execute a query and get back the exact same result. This property makes it perfect for caching too.&lt;/p&gt;
&lt;p&gt;Queries can be automatically cached and when the cache consumes too much memory, you can just clear it. The next invocation of the query will see that there is no cache entry anymore and will simply re-run its logic and cache the results again. It might just run a little slower once until the result is cached again. But will never return an incorrect result.&lt;/p&gt;
&lt;p&gt;But for the cache to be correct there is one crucial detail: It also needs to include the passed arg in the hashed cache key. This in turn means when &lt;code&gt;Query A&lt;/code&gt; is called in many places with multiple different arguments passed to it, each argument will create a new instance of that query with its own cached return value.&lt;/p&gt;
&lt;h3&gt;Shape of a Query&lt;/h3&gt;
&lt;p&gt;A query is typically defined as function with two arguments&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Database&lt;/li&gt;
&lt;li&gt;Argument (sometimes confusingly also referred to as input)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Expressed in TypeScript it would look like this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;type Query&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Database&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;arg&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;db&lt;/code&gt; parameter is where all queries live on and The &lt;code&gt;arg&lt;/code&gt; parameter is what you call the query with. To call a query inside a query you do &lt;code&gt;db.call_other_query(someArg)&lt;/code&gt;. To make this nicer and the database less of a thing to know about, most implementations add some sugar in the form of macros or decorators:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyDatabase&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Database&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;getTypeAtCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; offset&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Type &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getIdentifierAtCursor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; offset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTypeFromId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Inputs: The source of truth&lt;/h3&gt;
&lt;p&gt;When a file changes on disk you want to somehow tell the compiler that it should invalidate it so that the cache entry is cleared and the next time a query asks for it, it re-processes that file. This is done through &lt;code&gt;Inputs&lt;/code&gt;. An &lt;code&gt;Input&lt;/code&gt; is a stateful object that can be written to. Usually they live on the database too.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;directory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;ev&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ev&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;change&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; content &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readTextFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ev&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reading inputs in queries usually involves calling a method or accessing a special property on it.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyDatabase&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Database&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  files &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; FileInput&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Setter helper&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;updateFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; input &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;files&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;string&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;files&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	@query&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;parseFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;AST&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fileInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;files&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileInput &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Read input&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; code &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fileInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;To change or not to change&lt;/h2&gt;
&lt;p&gt;The key difference here compared to signals is that &lt;em&gt;nothing&lt;/em&gt; really happens we have written to an input. Queries are not automatically re-executed or anything like that. Contrary to Signals in UI programming which are typically some form of &amp;quot;live&amp;quot; subscriptions, Queries are not live.&lt;/p&gt;
&lt;p&gt;In a Signals system a change to a source signal marks it as dirty, and then it follows all active subscriptions and flags every derived/computed signal as dirty until it reaches the place where the subscription was triggered. The triggering part is typically called an &lt;code&gt;Effect&lt;/code&gt;. Changes are sort of pushed through the system and when it reaches an effect, it re-runs and &amp;quot;pulls&amp;quot; the new values. There are of course various optimization strategies that libraries employ, but that&#39;s out of scope for this article. What&#39;s important to remember is that they are essentially a push-pull system when it comes to writes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/signals-graph.jpg&quot; alt=&quot;Signals are push-pull&quot; /&gt;&lt;/p&gt;
&lt;p&gt;A push-pull architecture is perfect for UI rendering. Changes often need to be displayed instantly and you need to ensure that the whole screen is in sync. All signals rendered must always show the value of the same revision they were part of. The situation that the top half of the screen shows new values, whereas the bottom half still renders stale ones, should never occur. This is often referred to as a &amp;quot;glitch&amp;quot;. Pushing changes through the system is an elegant way to ensure that this never happens. It basically trades more memory with faster execution and being able to guarantee a glitch-free result.&lt;/p&gt;
&lt;p&gt;Query based compilers work differently, they are demand driven. You have to &lt;em&gt;ask&lt;/em&gt; for them to be re-executed. It&#39;s not as crucial that everything happens in the same tick. It&#39;s totally okay for the query for autosuggestions to return and the query for type errors to return a few milliseconds later. There is no need to keep some sort of screen in sync per frame. Correctness must be ensured, of course. But the timing guarantees are a bit more relaxed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/query-graph.jpg&quot; alt=&quot;Queries are pull only&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Always pushing changes through the system is too expensive because a query based compiler will easily have &amp;gt;100k nodes depending on the project size. At that scale, memory becomes a real performance concern. To reduce the memory consumption, dependencies are only tracked in one direction in a query based system compared to tracking both directions with signals.&lt;/p&gt;
&lt;h3&gt;The secret sauce: Revisions&lt;/h3&gt;
&lt;p&gt;But still, like with Signals, correctness is a hard requirement for query based systems. So if queries finish at different times, how is it guaranteed that the results are always correct? The key insight is that ultimately queries are a pure function over their inputs. Given the same inputs, the same result must occur. The result is therefore correct.&lt;/p&gt;
&lt;p&gt;Inside the system there is a global revision counter that is incremented with each changed input. Each node has a &lt;code&gt;changed_at&lt;/code&gt; and &lt;code&gt;verified_at&lt;/code&gt; field that can be used to check the status of the cached value.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Node&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	changed_at&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Revision&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	verified_at&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Revision&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	dependencies&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Node&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells us whether we can re-use the cached results of a node or not. That said, due to only tracking dependencies in one direction we need to always dirty check all dependencies of a query up to the leaf nodes, unless &lt;code&gt;verified_at&lt;/code&gt; is equal to the current revision which allows us to bail out early. When you reach a leaf node and realise that it hasn&#39;t changed at all, despite the global revision counter being increased, you just set all &lt;code&gt;verified_at&lt;/code&gt; properties of parent nodes to the current global revision. If the input or a query result &lt;em&gt;has&lt;/em&gt; changed you update both the &lt;code&gt;changed_at&lt;/code&gt; and &lt;code&gt;verified_at&lt;/code&gt; fields. But when somewhere a query still returns the same result despite its dependencies having changed, we also bail out and only update only &lt;code&gt;verified_at&lt;/code&gt; pointers while unwinding the stack.&lt;/p&gt;
&lt;h3&gt;What about threading?&lt;/h3&gt;
&lt;p&gt;This is one killer feature of query based systems. Depending on the programming language to compile, you can aggressively parallelize tasks like parsing files. If you guarantee that each query can only be executed by one thread at a time you can parallelize a lot of work. Since queries tend to be pretty granular you can often kill a thread and spawn it again with the latest revision. This is an aspect that I need to dive into deeper myself, but I find it interesting that restarting work is a possibility at all in such a system.&lt;/p&gt;
&lt;h2&gt;Which one is better?&lt;/h2&gt;
&lt;p&gt;It depends. Signals are better for UI rendering, but the query-based system is more appropriate for a compiler. It all depends on the use case. In either way I find it interesting that different systems sorta arrived at similar building blocks and concepts behind the scenes to achieve an incremental system.&lt;/p&gt;
&lt;p&gt;I&#39;m kinda wondering what our JavaScript tools would look like if they were built from the ground up to be incremental. What would something like vite look like if it was built as a query-based system? In spirit, a dev server is similar in that it is a living thing that we constantly query for data apart from HMR updates which are pushed from the server. Maybe a mix of both signals and a query architecture would be the golden ticket?&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Building From First Principles</title>
    <link href="https://marvinh.dev/blog/building-from-first-principles/"/>
    <updated>2025-10-28T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/building-from-first-principles/</id>
    <content type="html">&lt;p&gt;It&#39;s funny, sometimes I feel like I&#39;m living in two different programming worlds. There is the online world on Bluesky threads, GitHub, blog posts or other platforms where discussions are often dominated by the latest frameworks, the hottest libraries or discussion that feel very abstract. Then there is the other world I live in, the one I share with my local developer friends. And honestly? They&#39;re miles apart.&lt;/p&gt;
&lt;h2&gt;Online !== Offline&lt;/h2&gt;
&lt;p&gt;In the online realm, the idea of building anything substantial without any of the major frameworks is typically met with bewilderment, if not scorn. &amp;quot;Vanilla JS for an entire UI? Are you mad?&amp;quot; Building something with vanillajs and stringing a few libraries together as needed is immediately labelled as unmaintainable.&lt;/p&gt;
&lt;p&gt;But when I meet up with my developer friends the tech stack they use is the most boring one imagineable. Heck, when you ask them about that, they typically just shrug their shoulders. They couldn&#39;t care less. We&#39;re not talking about some simple demo app either. The apps they are working on are pretty involved from what they do. They are mission critical apps that have real users and solve a concrete business impact. And boy do they work! These are some of the most robust apps I&#39;ve ever seen.&lt;/p&gt;
&lt;h2&gt;Intentionally choosing boring tech&lt;/h2&gt;
&lt;p&gt;The funny bit: Despite not using any frameworks they are one of the most maintainable apps I&#39;ve had the pleasure to see the code of. They&#39;ve been able to bang out features much quicker than anyone else I know. We often ping pong some ideas around and the prototype typically follows a few hours later. In my career so far I had the luck to be able to work among such folks. This wasn&#39;t an isolated incident project either. What makes them different?&lt;/p&gt;
&lt;p&gt;The one thing they have all in common is that they approach anything they do from first principles. Their first thought isn&#39;t &amp;quot;Which framework should I use?&amp;quot; It&#39;s: &amp;quot;What is the actual problem we&#39;re trying to solve?&amp;quot; They think deeply about outcomes, about user experience and the simplest most direct path towards that goal. Technology is merely a means to an end, not the end in itself.&lt;/p&gt;
&lt;h2&gt;Clarity is key&lt;/h2&gt;
&lt;p&gt;Their code pretty much reflects this philosophy: Plain, boring and concise with no fluff. You can easily read it top to bottom without needing to jump around to get the bigger picture. There is very little noise there. There is this pragmatic minimalism to it. You won&#39;t find layers of abstractions for abstraction&#39;s sake. The biggest &amp;quot;debate&amp;quot; - if you could even call it that - usually revolves around how to structure data or where to put side effects.&lt;/p&gt;
&lt;p&gt;Does this mean frameworks are bad? No, far from it! The online tech discourse just tends to create a sense of inevitability around particular tools or methodologies. It&#39;s my offline peers that remind me that a lot of effective software development exists outside of online framework debates.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>The missing link in JavaScript tools</title>
    <link href="https://marvinh.dev/blog/unifying-js-tools/"/>
    <updated>2025-09-11T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/unifying-js-tools/</id>
    <content type="html">&lt;p&gt;Lately, my mind has been occupied with finding ways to make tools simpler to work with. Our tools of today present a pretty fractured architecture. The frontend layer has to deal with many languages at once, ranging from custom templating languages, importing CSS to JSX or plain JavaScript. Each tool, the linter, formatter, type checker, test runner and dev server typically demands its own unique plugin system and preprocessing logic to help it understand those workflows.&lt;/p&gt;
&lt;h2&gt;The template predicament&lt;/h2&gt;
&lt;p&gt;This makes it difficult to innovate frameworks and integrate them with today&#39;s tooling. Take &lt;code&gt;.svelte&lt;/code&gt; files as an example. How do we teach our linter to understand them? What about type checking? Everything needs its own plugin. But why?&lt;/p&gt;
&lt;p&gt;All of these plugins have in common that they get an input string and turn it back into valid JavaScript. To get type checking in Svelte, Vue or other custom languages to work, the code is transpiled in a way that TypeScript understands. The templating bits are converted to JSX and the rest to standard JS. Both JS and JSX can be type checked by TypeScript. When a type checking error is encountered we check the source maps, if it maps back to user code. If it does, report it, else ignore it.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ts&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleClick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		count &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hello, {name}!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;You have clicked {count} times.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;on:&lt;/span&gt;click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;{handleClick}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Click me&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br /&gt;	&lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid blue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pseudo example of what gets sent to the LSP (real world is different, but it&#39;s the same idea):&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; SvelteComponent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; init&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; safe_not_equal &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;svelte/internal&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; __svelte_script &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; count&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleClick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		count &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		props&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		data&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; count &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		methods&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; handleClick &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleClick &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; __svelte_script&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Hello, &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;You have clicked &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt; times.&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;handleClick&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Click me&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;		&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeScript can perfectly type check this.&lt;/p&gt;
&lt;h2&gt;A Virtual File System as the Unifying Layer&lt;/h2&gt;
&lt;p&gt;Imagine a world where we could modify files before handing them off to the type checker or our linter. Something like a virtual file system of some sorts that everyone could tap into. Runtimes could even use this to run your code directly. You could run your code as if it would be any other script:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;deno foo.svelte&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;node foo.svelte&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At its heart, this virtual file system is a shared processing pipeline that has quite a lot in common with typical bundler APIs. Because if you think about it, &lt;code&gt;resolveId()&lt;/code&gt; and &lt;code&gt;load() + transform()&lt;/code&gt; neatly map to file system operations.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bundler API&lt;/th&gt;
&lt;th&gt;File System API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;resolveId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;follow symlink&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;load (+ transform)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;readFile&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;resolveId&lt;/code&gt; function would be reinterpreted as resolving symlinks within this virtual file system. This allows for dynamic mapping and aliasing of files. Similarly, the &lt;code&gt;load&lt;/code&gt; and &lt;code&gt;transform&lt;/code&gt; phases, where code is read and modified, can be thought of as a &lt;code&gt;readFile&lt;/code&gt; call.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/tooling-plugins.png&quot; alt=&quot;Today: All tools need their own plugin. Tomorrow: All tools tap in the the same plugin pipeline?&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Cross-Language usage&lt;/h2&gt;
&lt;p&gt;Lots of tools are in the process of moving some of their pieces to Rust or other languages like GO. We&#39;re now in a situation where we need to account for multiple consuming languages. All of that could be solved by talking to the same processing pipeline via IPC.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I&#39;m not sure when or even if such a thing will ever see the light of day. I would welcome it though, because it would make experimenting with new templating languages or other DSL&#39;s in the JS ecosystem much easier. I want to be able to take my pre-processing pipeline with me, regardless of which test runner, linter or formatter I want to use.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Speeding up the JavaScript ecosystem - Semver</title>
    <link href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-12/"/>
    <updated>2025-08-10T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-12/</id>
    <content type="html">&lt;p&gt;Whilst dabbling with the &lt;a href=&quot;https://github.com/preactjs/preact&quot;&gt;Preact&lt;/a&gt; repo I noticed that running &lt;code&gt;npm install&lt;/code&gt; takes more than 3s. This seems excessively long and piqued my interest. I was waiting for a good use case to try out &lt;a href=&quot;https://discoveryjs.github.io/cpupro/&quot;&gt;cpupro&lt;/a&gt; anyway, so this seemed like a perfect fit.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-semver.png&quot; alt=&quot;Cpuprof shows how much CPU team each package consumed. The semver package alone consumes 600ms of the 3s total time.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;tar&lt;/code&gt; library is kinda expected to be up there as a decent amount of time is spent extracting tarballs. What captured my attention was the &lt;code&gt;semver&lt;/code&gt; package though. It&#39;s the package that compares all version ranges of all the dependencies in your project to figure out which versions to install. The versioning is based on the &lt;a href=&quot;https://semver.org/&quot;&gt;semver&lt;/a&gt; standard which defines a major, minor, patch and an optional prerelease component:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;.3 &lt;span class=&quot;token comment&quot;&gt;# major.minor.patch&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token number&quot;&gt;2.0&lt;/span&gt;.0-alpha.3 &lt;span class=&quot;token comment&quot;&gt;# major.minor.patch-prerelease&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Package managers go beyond that to allow you to specify ranges or constraints. Instead of only allowing exact versions, they allow you to pass something like &lt;code&gt;^1.0.0&lt;/code&gt; which means: &amp;quot;Allow any minor or patch version bigger than this version. Ranges can also be composed. You&#39;ll often find this sort of format when declaring &lt;code&gt;peerDepdendencies&lt;/code&gt;: &lt;code&gt;1.x || 2.x&lt;/code&gt;. Npm supports a &lt;a href=&quot;https://github.com/npm/node-semver?tab=readme-ov-file#advanced-range-syntax&quot;&gt;plethora of ways&lt;/a&gt; to express desired version ranges for your dependencies.&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;.3  &lt;span class=&quot;token comment&quot;&gt;# =1.2.3&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token number&quot;&gt;4.0&lt;/span&gt;.0-alpha.4   &lt;span class=&quot;token comment&quot;&gt;# =4.0.0-alpha.4&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token number&quot;&gt;4.0&lt;/span&gt;.0-2  &lt;span class=&quot;token comment&quot;&gt;# =4.0.0-2&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Partial versions are also possible&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;# =1.0.0&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# =1.2.0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# Ranges&lt;/span&gt;&lt;br /&gt;^2.0.0 &lt;span class=&quot;token comment&quot;&gt;# &gt;= 2.0.0 &amp;amp;&amp;amp; &amp;lt;3.0.0&lt;/span&gt;&lt;br /&gt;~2.0.0 &lt;span class=&quot;token comment&quot;&gt;# &gt;= 2.0.0 &amp;amp;&amp;amp; &amp;lt;2.1.0&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;.x &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;.x &lt;span class=&quot;token comment&quot;&gt;# &gt;=1.0.0 || &gt;=2.0.0&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;.3 - &lt;span class=&quot;token number&quot;&gt;1.4&lt;/span&gt;.0 &lt;span class=&quot;token comment&quot;&gt;# &gt;=1.2.3 &amp;amp;&amp;amp; &amp;lt;=1.4.0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;.x &lt;span class=&quot;token comment&quot;&gt;# &gt;=1.0.0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;# ...and more&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Parse, don&#39;t validate&lt;/h2&gt;
&lt;p&gt;To get some idea how often the &lt;code&gt;npm&lt;/code&gt; binary calls into &lt;code&gt;semver&lt;/code&gt;, I&#39;ve cloned the npm repository, ran &lt;code&gt;npm install&lt;/code&gt; and wrapped all exports of the &lt;code&gt;semver&lt;/code&gt; library with functions that log out which function was called and with what arguments.&lt;/p&gt;
&lt;p&gt;Doing an installation in the &lt;code&gt;preact&lt;/code&gt; repo calls about &lt;a href=&quot;https://gist.github.com/marvinhagemeister/2c3113d665fb6e4030d89a2062dd1dbc&quot;&gt;21.2k times in total&lt;/a&gt; into the &lt;code&gt;semver&lt;/code&gt;. That&#39;s way more than I would&#39;ve expected. Here are some stats of the functions called:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Callcount&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;satisfies&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;11151&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;valid&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;4878&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validRange&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;4911&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sort&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;156&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rcompare&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;79&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;simplifyRange&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;24&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Looking at the table we can see that the call count is entirely dominated by validating and checking if version constraints are satisfied. And that makes sense, because that&#39;s what a package manager is supposed to do during installation of dependencies. Looking at the raw function call logs we can see a common pattern:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;valid &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;^6.0.0&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;validRange &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;^6.0.0&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;satisfies &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;6.0.0&#39;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&#39;^6.0.0&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;valid &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;^0.5.0&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;validRange &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;^0.5.0&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;satisfies &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;0.5.0&#39;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&#39;^0.5.0&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;valid &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;^7.26.0&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;validRange &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;^7.26.0&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;satisfies &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;7.26.0&#39;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&#39;^7.26.0&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;valid &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;^7.25.9&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;validRange &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;^7.25.9&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;satisfies &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;7.25.9&#39;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&#39;^7.25.9&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;valid &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;^7.25.9&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;validRange &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;^7.25.9&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;satisfies &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;7.25.9&#39;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&#39;^7.25.9&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;valid &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;^7.26.0&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;validRange &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;^7.26.0&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;satisfies &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;7.26.0&#39;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&#39;^7.26.0&#39;&lt;/span&gt;, &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A huge portion of the &lt;code&gt;satisfies&lt;/code&gt; calls are preceded by a call to both &lt;code&gt;valid&lt;/code&gt; and &lt;code&gt;validRange&lt;/code&gt;. This hints at us that these two functions are used to validate the arguments passed to &lt;code&gt;satisfies&lt;/code&gt;. Since the source of the npm cli is public we can check the source code for what these functions are doing.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;valid&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;version&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; v &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;version&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; v &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;version &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;validRange&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;range&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// Return &#39;*&#39; instead of &#39;&#39; so that truthiness works.&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// This will throw if it&#39;s invalid anyway&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;range&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;range &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;er&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;satisfies&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;version&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; range&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		range &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;range&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;er&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; range&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;version&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To validate input correctness, both validator functions parse the input data, allocate the resulting data structure and return back the original input string if it passed the check. When we call the &lt;code&gt;satisfies&lt;/code&gt; function, we do the same work that we just threw away again. This is a classic case of doing twice the work necessary due to not following the &lt;a href=&quot;https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/&quot;&gt;&amp;quot;Parse, don&#39;t validate&amp;quot;&lt;/a&gt; rule.&lt;/p&gt;
&lt;p&gt;Everytime you&#39;re dealing with parsers and you&#39;re validating the inputs before doing the parsing you&#39;re burning unnecessary CPU cycles. Parsing itself &lt;strong&gt;&lt;em&gt;is&lt;/em&gt;&lt;/strong&gt; a form of validation. It&#39;s pointless. By getting rid of the duplicate validation we can save about &lt;code&gt;9.8k&lt;/code&gt; function calls.&lt;/p&gt;
&lt;p&gt;With us having logged the function calls prior, we have an ideal benchmark case and can measure the timings with and without duplicate validation. For that I&#39;m using the &lt;a href=&quot;https://docs.deno.com/runtime/reference/cli/bench/&quot;&gt;&lt;code&gt;deno bench&lt;/code&gt;&lt;/a&gt; tool.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;benchmark&lt;/th&gt;
&lt;th&gt;time/iter (avg)&lt;/th&gt;
&lt;th&gt;iter/s&lt;/th&gt;
&lt;th&gt;(min … max)&lt;/th&gt;
&lt;th&gt;p75&lt;/th&gt;
&lt;th&gt;p99&lt;/th&gt;
&lt;th&gt;p995&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;validate + parse&lt;/td&gt;
&lt;td&gt;99.9 ms&lt;/td&gt;
&lt;td&gt;10.0&lt;/td&gt;
&lt;td&gt;( 85.5 ms … 177.3 ms)&lt;/td&gt;
&lt;td&gt;91.1 ms&lt;/td&gt;
&lt;td&gt;177.3 ms&lt;/td&gt;
&lt;td&gt;177.3 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;parse&lt;/td&gt;
&lt;td&gt;28.0 ms&lt;/td&gt;
&lt;td&gt;35.7&lt;/td&gt;
&lt;td&gt;( 22.8 ms … 73.3 ms)&lt;/td&gt;
&lt;td&gt;26.4 ms&lt;/td&gt;
&lt;td&gt;73.3 ms&lt;/td&gt;
&lt;td&gt;73.3 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Removing the duplicate validation gives us a nice speedboost already. It makes the code &lt;code&gt;~70%&lt;/code&gt; faster.&lt;/p&gt;
&lt;h2&gt;Does slapping a cache in front of things help?&lt;/h2&gt;
&lt;p&gt;Over time the folks working on the &lt;code&gt;semver&lt;/code&gt; library realized this problem and added an &lt;code&gt;LRU&lt;/code&gt; cache, which is sorta a special variant of a &lt;code&gt;Map&lt;/code&gt; in JavaScript terms that has a limited number of entries. This is done to avoid the &lt;code&gt;Map&lt;/code&gt; growing indefinitely in size. And it turns out it&#39;s somewhat effective at speeding things up. Logging out whether a cache entry was found or missed reveals that only &lt;code&gt;523&lt;/code&gt; times out of &lt;code&gt;26k&lt;/code&gt; calls returned a non-cached result. That&#39;s pretty good!&lt;/p&gt;
&lt;p&gt;If we remove the caching we can get an idea of how much time it saved.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-semver-cache.png&quot; alt=&quot;With the LRU cache removed the semver package consumes 731ms&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In total, adding the cache saved about &lt;code&gt;133ms&lt;/code&gt;. Which I guess is not nothing, but far from being effective in the grand picture, despite the amount of cache hits.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Uncached&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;&lt;code&gt;731ms&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cached&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;&lt;code&gt;598ms&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Caches are a bit of a pet peeve of mine. They can be incredibly useful if used sparingly at the right places, but they are notoriously overused across the JavaScript ecosystem. It often feels like a lazy way to increase performance, where instead of making your code fast, you try to cache the results and kinda hope for the best. Making your code fast instead, yields much better results in many cases.&lt;/p&gt;
&lt;p&gt;Caches should be seen as the last form of measure to take when all other options have been exhausted.&lt;/p&gt;
&lt;h2&gt;How fast can we make it?&lt;/h2&gt;
&lt;p&gt;With semver having a small grammar to parse, I kinda felt nerdsniped to have a go at it myself. Over the course of a day I wrote a semver parser and added support for all the ways npm allows you to specify a semver constraint. The code is doing nothing out of the ordinary. It loops over the input string, detects the relevant tokens and creates the final data structures out of that. Anyone familiar with parsers would&#39;ve come up with the same code. It&#39;s doing nothing special. No secret JavaScript performance tricks or anything.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseSemver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Semver &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ctx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; major &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;charCodeAt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ch &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; Char&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;n0 &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; ch &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; Char&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;n9&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		major &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;invalidErr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	ch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expectDot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; minor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ch &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; Char&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;n0 &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; ch &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; Char&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;n9&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		minor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;invalidErr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ...and so on&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then we need some code that can check if a version satisfies semver constraints, which is structured in a similar way.&lt;/p&gt;
&lt;p&gt;With us having logged all function calls prior, we have the ideal dataset for a benchmark. For the first one, although I&#39;ve kept the validate + parse steps.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;benchmark&lt;/th&gt;
&lt;th&gt;time/iter (avg)&lt;/th&gt;
&lt;th&gt;iter/s&lt;/th&gt;
&lt;th&gt;(min … max)&lt;/th&gt;
&lt;th&gt;p75&lt;/th&gt;
&lt;th&gt;p99&lt;/th&gt;
&lt;th&gt;p995&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;node-semver&lt;/td&gt;
&lt;td&gt;99.9 ms&lt;/td&gt;
&lt;td&gt;10.0&lt;/td&gt;
&lt;td&gt;( 85.5 ms … 177.3 ms)&lt;/td&gt;
&lt;td&gt;91.1 ms&lt;/td&gt;
&lt;td&gt;177.3 ms&lt;/td&gt;
&lt;td&gt;177.3 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;custom&lt;/td&gt;
&lt;td&gt;3.9 ms&lt;/td&gt;
&lt;td&gt;255.3&lt;/td&gt;
&lt;td&gt;( 3.7 ms … 6.1 ms)&lt;/td&gt;
&lt;td&gt;4.0 ms&lt;/td&gt;
&lt;td&gt;4.7 ms&lt;/td&gt;
&lt;td&gt;6.1 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Even &lt;em&gt;with&lt;/em&gt; doing the unnecessary validate step, the custom parser is &lt;code&gt;25x&lt;/code&gt; faster. If we capture a CPU profile of the amount of work done, the difference is even more stark.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-semver-node-trace.png&quot; alt=&quot;CPU profile of node&#39;s semver library shows many function calls.&quot; /&gt;
&lt;img src=&quot;https://marvinh.dev/media/js-tools-semver-custom-trace.png&quot; alt=&quot;CPU profile of the custom parser contains much fewer function calls and is less noisier.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Let&#39;s get rid of the unnecessary validation step for the next benchmark for both of them.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;benchmark&lt;/th&gt;
&lt;th&gt;time/iter (avg)&lt;/th&gt;
&lt;th&gt;iter/s&lt;/th&gt;
&lt;th&gt;(min … max)&lt;/th&gt;
&lt;th&gt;p75&lt;/th&gt;
&lt;th&gt;p99&lt;/th&gt;
&lt;th&gt;p995&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;node-semver&lt;/td&gt;
&lt;td&gt;28.0 ms&lt;/td&gt;
&lt;td&gt;35.7&lt;/td&gt;
&lt;td&gt;( 22.8 ms … 73.3 ms)&lt;/td&gt;
&lt;td&gt;26.4 ms&lt;/td&gt;
&lt;td&gt;73.3 ms&lt;/td&gt;
&lt;td&gt;73.3 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;custom&lt;/td&gt;
&lt;td&gt;2.9 ms&lt;/td&gt;
&lt;td&gt;347.2&lt;/td&gt;
&lt;td&gt;( 2.7 ms … 6.4 ms)&lt;/td&gt;
&lt;td&gt;2.9 ms&lt;/td&gt;
&lt;td&gt;3.4 ms&lt;/td&gt;
&lt;td&gt;6.4 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;With a level playing field of not doing unnecessary validation the difference is much smaller, but still in the order of &lt;code&gt;10x&lt;/code&gt; faster. So in summary the semver checks can be made &lt;code&gt;33x&lt;/code&gt; faster than they are now.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;As usual, the tools in the JavaScript ecosystem aren&#39;t slow because of the language, but because of slow code. At the time of this writing, the &lt;code&gt;semver&lt;/code&gt; library is used in all popular package managers. It&#39;s used in &lt;code&gt;npm&lt;/code&gt;, &lt;code&gt;yarn&lt;/code&gt; and &lt;code&gt;pnpm&lt;/code&gt;. Making this faster or switching to a faster alternative would speed the installation process up considerably for all of them.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Speeding up the JavaScript ecosystem - Rust and JavaScript Plugins</title>
    <link href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-11/"/>
    <updated>2025-02-23T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-11/</id>
    <content type="html">&lt;p&gt;Over the past year (2024) there has been a strong movement to rewrite JavaScript tools in Rust to make them faster. Rust is well suited for this as it runs much closer to hardware and doesn&#39;t rely on garbage collection. This makes it an ideal candidate for computationally intensive tasks. Linting in its basic form is such a task, as it involves parsing and traversing lots of source code.&lt;/p&gt;
&lt;p&gt;But there is a big elephant in the room: Plugins.&lt;/p&gt;
&lt;h2&gt;The Rust plugin dilemma&lt;/h2&gt;
&lt;p&gt;To me the main reason the JavaScript ecosystem has flourished in the past years is the pluggability of our tools. You want to explore a custom template syntax for your framework? Just write your own babel plugin. You want to extend ESlint with custom rules? Just write a plugin. The list goes on and on.&lt;/p&gt;
&lt;p&gt;Rust on the other hand being a different language is less accessible to JavaScript developers. There is a shared concern that by interfacing with JavaScript you&#39;d lose the performance gains achieved with going with Rust in the first place due to the overhead of serializing and deserializing messages between the two languages. And that&#39;s for good reason, that part is costly.&lt;/p&gt;
&lt;p&gt;For this reason there is a strong desire to stay in Rust-land as much as possible. Some tooling developers even go so far as ditching JavaScript entirely and favouring some form of query language like &lt;a href=&quot;https://docs.grit.io/tutorials/gritql&quot;&gt;GritQL&lt;/a&gt; as a way to extend their tools. But this in turn creates new problems: No JavaScript developer knows that query language. How do you debug it? How do you test it? etc.&lt;/p&gt;
&lt;p&gt;Is there any chance JavaScript plugins can ever be made fast together with Rust?&lt;/p&gt;
&lt;h2&gt;Serialization and deserialization costs&lt;/h2&gt;
&lt;p&gt;Solving this exact problem became my main task at the end of 2024. We wanted to add support for JavaScript plugins in Deno&#39;s built in linter. The goal was to have a plugin system that is compatible with &lt;a href=&quot;https://eslint.org/&quot;&gt;ESLint&lt;/a&gt;, which means the AST format we&#39;re going to use is &lt;a href=&quot;https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/ast-spec/README.md&quot;&gt;TSESTree&lt;/a&gt; which itself is a superset of &lt;a href=&quot;https://github.com/estree/estree&quot;&gt;ESTree&lt;/a&gt; with support for TypeScript and JSX.&lt;/p&gt;
&lt;p&gt;The first prototype relied on plain JSON as the message format. It&#39;s well supported and pretty much every language ships with an optimized JSON parser out of the box. As a test case we picked the &lt;a href=&quot;https://github.com/microsoft/TypeScript/blob/main/src/compiler/checker.ts&quot;&gt;&lt;code&gt;checker.ts&lt;/code&gt; file in TypeScript&#39;s code base&lt;/a&gt;. It contains &lt;code&gt;53036&lt;/code&gt; lines of code and ends up creating &lt;code&gt;610k&lt;/code&gt; AST nodes in total. Enough to stress test the serialization and deserialization aspect.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Time (in s)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;serde-json&lt;/code&gt; + &lt;code&gt;JSON.parse()&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;2.91s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;And yeah, as expected, it&#39;s slow. However, this gives us a good reference point to compare future optimizations to. Whilst there are various strategies to reduce the serialization overhead by going with a custom format, we need something that&#39;s multiple order of magnitudes faster.&lt;/p&gt;
&lt;p&gt;What if we could get rid of the deserialization overhead entirely? What if the JavaScript side could traverse the AST tree by walking the received buffer directly without ever having to deserialize it? Instead of creating &lt;code&gt;610k&lt;/code&gt; AST nodes, we would create zero nodes on the JavaScript side.&lt;/p&gt;
&lt;h2&gt;Flattening the AST&lt;/h2&gt;
&lt;p&gt;The tricky bit is that ASTs are a tree structure. They&#39;re composed of objects pointing to other objects and we somehow need to represent it as a flat list. That flat list needs to be able to be expressed in numbers in a buffer, like a &lt;code&gt;Uint8Array&lt;/code&gt;. What&#39;s worse is that every node has different amounts of object properties and few have a similar shape.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Input: if (condition) { foo() }&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ast &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;IfStatement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;condition&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;consequent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BlockStatement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ExpressionStatement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token literal-property property&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CallExpression&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;alternate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you flatten a recursive structure, you typically have to either insert start and end markers in your message or pre-calculate offsets to the relevant nodes in your buffer. This is necessary to know when a node begins and ends. It&#39;s doable but that bookkeeping overhead quickly becomes annoying to keep track of.&lt;/p&gt;
&lt;p&gt;A much more elegant approach to this problem is to store every AST node in an array and leverage the index into that array as the node&#39;s &lt;code&gt;id&lt;/code&gt;. Instead of having nodes pointing to other nodes directly, you&#39;ll point to other nodes via its &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ast &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// First node at index 0 is a placeholder as we&#39;ll use the&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// index 0 as an empty value&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;IfStatement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token literal-property property&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Points to index 2&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token literal-property property&quot;&gt;consequent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Points to index 3&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token literal-property property&quot;&gt;alternate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Empty, points to placeholder node at index 0&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BlockStatement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ...etc&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This flattens the tree so that we don&#39;t have to deal with the recursiveness anymore. We still need to deal with the variable amount of properties though. Consequently, a way to make every node the same shape is needed.&lt;/p&gt;
&lt;p&gt;For our use case of linting the AST tree, we can safely assume that walking the AST tree is the most common operation. We need to visit parent nodes first and then all their children in order. But we don&#39;t really care under &lt;em&gt;which&lt;/em&gt; property a child node can be found. What matters is &lt;em&gt;that&lt;/em&gt; we visit all child nodes in the correct order.&lt;/p&gt;
&lt;p&gt;We can exploit that aspect by extracting out all properties into a separate data structure. The nodes itself will only keep the &lt;code&gt;type&lt;/code&gt;, a &lt;code&gt;child&lt;/code&gt;, &lt;code&gt;next&lt;/code&gt; and &lt;code&gt;parent&lt;/code&gt; pointer. Theoretically, you could drop the &lt;code&gt;parent&lt;/code&gt; pointer, but it&#39;s going to make our life easier because linting rules in ESLint can traverse upwards at any time. The node itself only contains information how to traverse it, without any knowledge of its properties.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ast &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;consequent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;alternate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;nodes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// First node at index 0 is a placeholder as we&#39;ll use the&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// index 0 as an empty value&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;IfStatement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Points to index 2&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Point to next child of IfStatement&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Parent node is IfStatement&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BlockStatement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Parent node is IfStatement&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// ...etc&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the new shape, we can traverse a node and its children by walking the &lt;code&gt;child&lt;/code&gt; pointers, followed by iterating over the &lt;code&gt;next&lt;/code&gt; pointers. Traversal becomes very simple and can be expressed with very few lines of code.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;traverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;buf&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; idx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Check if a lint rule matches&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buf&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; idx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;visit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buf&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; idx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Traverse over children&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; childIdx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buf&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; idx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;childIdx &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;traverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buf&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; childIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Traverse next siblings&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; nextIdx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readNext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buf&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; idx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nextIdx &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;traverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buf&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nextIdx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The reason traversal has become so simple is that the node&#39;s data structure only ever contains traversable properties like &lt;code&gt;child&lt;/code&gt; and &lt;code&gt;next&lt;/code&gt;. We never have to check if a property is traversable or not anymore. This seems like a minor detail, but reduces the number of checks you have to do during traversal and typically makes code simpler.&lt;/p&gt;
&lt;p&gt;The only part of variable length left is the &lt;code&gt;type&lt;/code&gt; property. We can get rid of that variableness by storing strings in a string table and referring to a string by its index into that table instead. This has the neat benefit that it also compresses our message at the same time, because we only ever store a particular string once in our message.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ast &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;stringTable&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;IfStatement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BlockStatement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;consequent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;alternate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;nodes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// First node at index 0 is a placeholder as we&#39;ll use the&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// index 0 as an empty value&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// StringTable index 1&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Points to index 2&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// StringTable index 2&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Point to next child of IfStatement&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Parent node is IfStatement&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// StringTable index 3&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;child&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Parent node is IfStatement&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// ...etc&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Effectively, our new format allows us to represent a node with just 4 numbers. We can drop the object property names and flatten the data even further by storing all numbers in the same array. Each node has the same length which allows us to easily calculate the offset of each node on the fly. We don&#39;t need to keep track of offsets anymore as the offset and the fixed size of a node gives us enough information to know where a node starts and ends.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Empty placeholder node&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// type&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// child&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// next&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// parent&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// IfStatement&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// type&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// child&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// next&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// parent&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Identifier&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// type&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// child&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// next&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// parent&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// BlockStatement&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// type&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// child&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// next&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// parent&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get a node&#39;s offset in the buffer, we&#39;ll use this simple formula: &lt;code&gt;index * 4&lt;/code&gt;. It&#39;s as simple as that. Doing this calculation on the fly costs practically nothing.&lt;/p&gt;
&lt;h2&gt;Lazy deserialization&lt;/h2&gt;
&lt;p&gt;So far we&#39;ve only focused on the traversal aspect, but when a lint rule matches we still need to materialize an actual object for the lint rule to consume. We don&#39;t want to expose the buffer directly to users and in fact they should be completely unaware of that.&lt;/p&gt;
&lt;p&gt;However, we want to be really careful to only materialize nodes that are actually needed. If we&#39;d materialize a node and all its children upfront we&#39;d nullify a huge portion of the optimization work we&#39;ve put in so far.&lt;/p&gt;
&lt;p&gt;A solution to this problem is to go with a facade class that looks like a node, but has a bunch of getters &lt;code&gt;getters&lt;/code&gt; that surf over the underlying buffer instead. When you access a &lt;code&gt;getter&lt;/code&gt; in a lint rule that points to another node, we&#39;ll materialize that one on demand. We will only ever materialize a node the user asked for, never more.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NodeFacade&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; stringTable&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; propertyTable&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;#buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; buffer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;#stringTable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; stringTable&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;#propertyTable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; propertyTable&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;#index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; typeNum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;#buffer&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;#index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;#stringTable&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;typeNum&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; properties &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;#propertyTable&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; property &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; properties&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNodeIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;property&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NodeFacade&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;#buffer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;#stringTable&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;#propertyTable&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;				property&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;property&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ... more getters here&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since there are way less than 255 unique properties for all AST nodes (JavaScript + TypeScript + JSX) combined, we can go even one step further and simply create getters for every property upfront (thanks to &lt;a href=&quot;https://bsky.app/profile/jviide.iki.fi&quot;&gt;@jviide&lt;/a&gt; for that tip!). This might seem like an odd choice, because not every node has every property, but we can pretend to only have specific properties through typing it differently in TypeScript.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Visitor&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;Identifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;IfStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;IfStatment&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Expression&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		consequent&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Statement&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		alternate&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Statement&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;BlockStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;BlockStatement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; body&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Statement&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From a typing perspective the lint rule always gets the correct object, but in reality the object is an instance of our &lt;code&gt;NodeFacade&lt;/code&gt; class and has many more properties. The genius part here is that our code gets a lot shorter. We don&#39;t need to declare 170+ different classes for every AST node type. We just need a single one. We&#39;ve hidden the fact that we&#39;re just surfing over a buffer object behind the scenes completely.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; eslintPlugin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-plugin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	rules&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token string-property property&quot;&gt;&quot;my-rule&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token function&quot;&gt;IfStatement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;						&lt;span class=&quot;token comment&quot;&gt;// Just an example&lt;/span&gt;&lt;br /&gt;						&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;							ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;								node&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;test&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;								message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Don&#39;t pass &#39;foo&#39; to an IfStatement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;							&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;						&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With all optimizations in place the number looks much better. Sure, there is still some overhead of running plugins compared to not running them at all, but it&#39;s gotten fast enough to be usable at this point.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Time (in s)&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Speedup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;serde-json&lt;/code&gt; + &lt;code&gt;JSON.parse()&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;2.91s&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;custom&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;0.62s&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;~4.7x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The whole traversal and selector matching code only takes &lt;code&gt;20.4ms&lt;/code&gt; for &lt;code&gt;610k&lt;/code&gt; AST nodes. Most files have much less nodes, like around ~6000 nodes or fewer. Nearly all time is now exclusively consumed by the serialization step. There are some optimizations still left to explore that I haven&#39;t gotten around to. But for now it&#39;s fast enough.&lt;/p&gt;
&lt;p&gt;Here is a quick demo of the lint plugin system in action:&lt;/p&gt;
&lt;div style=&quot;margin: 0 auto; max-width: 860px&quot;&gt;
&lt;video controls=&quot;&quot; loop=&quot;&quot;&gt;
  &lt;source src=&quot;https://marvinh.dev/media/deno-lint-demo.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;/div&gt;
&lt;h2&gt;Bonus: Supporting all the languages&lt;/h2&gt;
&lt;p&gt;With a generic &lt;code&gt;NodeFacade&lt;/code&gt; class we now have no aspect in our data format that&#39;s tied to the TSESTree or ESTree format anymore. The data format has no knowledge of &lt;em&gt;what&lt;/em&gt; it contains. It just knows that it&#39;s a bunch of objects with a specific hierarchy encoded in them. It&#39;s completely independent of any language. We could pass in a CSS AST tree, a markdown AST tree or any AST from any other language and the plugin system would support it out of the box.&lt;/p&gt;
&lt;p&gt;The key thing that enabled us to do that was to encode tree traversal in a generic way in our data format.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So there you have it. Rust can work together with JavaScript plugins in a performant way. I think that this is the future of JavaScript tooling. Keeping pluggability alive is very important to me and I think for the ecosystem at large. All this work described here was done in a span of two months and shipped in &lt;a href=&quot;https://deno.com/blog/v2.2&quot;&gt;Deno 2.2&lt;/a&gt;. There is still some work left to do to support ESLint&#39;s config format, but you can already run (or write) raw ESLint plugins and have them run with &lt;code&gt;deno lint&lt;/code&gt;. &lt;a href=&quot;https://bsky.app/profile/bartlomieju.bsky.social&quot;&gt;Bartek&lt;/a&gt; even wired everything through the rest of Deno so that you can apply fixes, and see suggestions from your lint plugins right in your editor.&lt;/p&gt;
&lt;p&gt;All in all, I think Rust is here to stay in our JavaScript tools. I&#39;m excited to see where the future leads us and how we can make both Rust and JavaScript work even better together.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>The modern way to write JavaScript servers</title>
    <link href="https://marvinh.dev/blog/modern-way-to-write-javascript-servers/"/>
    <updated>2025-01-25T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/modern-way-to-write-javascript-servers/</id>
    <content type="html">&lt;p&gt;If you&#39;ve visited Node&#39;s homepage at some point in your life you have probably seen this snippet:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; createServer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node:http&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeHead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;text/plain&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello World!&#92;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// starts a simple http server locally on port 3000&lt;/span&gt;&lt;br /&gt;server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;127.0.0.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Listening on 127.0.0.1:3000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It shows how to create a plain web server and respond with plain text. This the API Node became famous for and it was kinda revolutionary when it came out. These days this call might be abstracted away behind frameworks, but under the hood, this is what they&#39;re all doing.&lt;/p&gt;
&lt;p&gt;It&#39;s nice, it works and a whole ecosystem has formed around that. However, it&#39;s not all roses. A pretty big downside of this API is that you &lt;strong&gt;have&lt;/strong&gt; to bind a socket and actually spawn the server. For production usage this is perfectly fine, but quickly becomes a hassle when it comes to testing or interoperability. Sure, for testing we can abstract away all that boilerplate like &lt;code&gt;supertest&lt;/code&gt; does, but even so it cannot circumvent having to bind to an actual socket.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Example supertest usage&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/user&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What if there was a better API that doesn&#39;t need to bind to a socket?&lt;/p&gt;
&lt;h2&gt;Request, Response and Fetch&lt;/h2&gt;
&lt;p&gt;Enter the modern times with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API&quot;&gt;&lt;code&gt;fetch&lt;/code&gt;&lt;/a&gt;-API that you might be already familiar with from browsers.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://example.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; html &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;fetch()&lt;/code&gt;-call returns a bog standard JavaScript class instance. But the kicker is that the same is true for the request! So if we zoom out a little this API allows every server to be expressed as a function that takes a &lt;code&gt;Request&lt;/code&gt; instance and returns a &lt;code&gt;Response&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyApp&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Response&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which in turn means you &lt;em&gt;don&#39;t&lt;/em&gt; need to bind sockets anymore! You can literally just create a new &lt;code&gt;Request&lt;/code&gt; object and call your app with that. Let&#39;s see what that looks like. This is our app (I know it&#39;s a boring one!):&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;myApp&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello world!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...and this is how we can test it:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// No fetch testing library needed&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;myApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://localhost/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello world!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are no sockets involved or anything like that. We don&#39;t even need an additional library. It&#39;s just plain old boring JavaScript. You create an object and pass it to a function. Nothing more, nothing less. So, how much overhead does binding to sockets end up being?&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benchmark&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;time/iter (avg)&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;iter/s&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Node-API&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;806.5 µs&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;1,240&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request/Response-API&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;3.0 µs&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;329,300&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Well, it turns out quite a bit! This benchmark might make it look like the difference are miniscule, but when let&#39;s say have a test suite that runs this server a thousand times the differences become more stark:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Spawn 1000x&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;time/ms&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;speedup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Node-API&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;1.531s&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request/Response-API&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;5.29ms&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;&lt;strong&gt;289x faster&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Going from &lt;code&gt;1.5s&lt;/code&gt; to &lt;code&gt;5.2ms&lt;/code&gt; which is practically instant, made it much more enjoyable to work on the tests.&lt;/p&gt;
&lt;h2&gt;How do I launch my server?&lt;/h2&gt;
&lt;p&gt;Now, to be fair, so far we haven&#39;t launched the server yet. And that&#39;s exactly the beauty of this API, because we didn&#39;t need to! The exact API depends on the runtime in use, but usually it&#39;s nothing more than a few lines. In Deno it looks like this, for example:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;Deno&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;serve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello world!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What&#39;s way more important than this though is that this API shape works across runtimes. Some use the &lt;code&gt;export default fetch&lt;/code&gt; syntax, others use a function call, or an event listener API style. Regardless of what the function is named, the underlying API shape for your code stays the same. You have a function which receives a &lt;code&gt;Request&lt;/code&gt; instance and you respond with a &lt;code&gt;Response&lt;/code&gt; instance.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CommonAPI&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Response &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Response&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hello world&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;EDIT: A previous iteration of this article mistakenly said that it this way of responding servers was a proper standard by WinterTC. This is not an official standard at the time of this writing. Different runtimes have just arrived at the same conclusion and made it easy to share code across them.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This API works everywhere today! The only exception here is Node and although they&#39;re part of WinterTC they haven&#39;t shipped this yet. But with a little good ol &lt;a href=&quot;https://gist.github.com/marvinhagemeister/cc236ec97235ce0305ae9d48a24a607d&quot;&gt;polyfilling&lt;/a&gt; you can teach it the modern ways to build servers. Once Node supports this natively, a bunch of tooling for frameworks will become easier.&lt;/p&gt;
&lt;p&gt;They too, tend to try to turn every runtime into Node which is a big task and causes lots of friction.. That&#39;s the exact scenario I ran into when &lt;a href=&quot;https://bsky.app/profile/marvinh.dev/post/3lgb5i27ufc2g&quot;&gt;writing adapters for frameworks for Deno&lt;/a&gt; and prototyping new APIs.&lt;/p&gt;
&lt;p&gt;Shoutout to SvelteKit which is one of the modern frameworks that nailed this aspect and made writing an adapter a breeze!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Speeding up the JavaScript ecosystem - Isolated Declarations</title>
    <link href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-10/"/>
    <updated>2024-07-06T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-10/</id>
    <content type="html">&lt;p&gt;Unbeknownst to many, the new &lt;code&gt;isolatedDeclaration&lt;/code&gt; feature shipped in &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/#isolated-declarations&quot;&gt;TypeScript 5.5&lt;/a&gt; is much more important than you might realize. It just revolutionized how we package and distribute JavaScript code. You don&#39;t need to create &lt;code&gt;*.d.ts&lt;/code&gt; files manually anymore by invoking the &lt;code&gt;tsc&lt;/code&gt; compiler. &amp;quot;Go to source&amp;quot; (the thing when you do &lt;code&gt;ctrl+click&lt;/code&gt; or &lt;code&gt;cmd+click&lt;/code&gt; on macOS) actually works now and it leads you right to the TypeScript source code instead of a &lt;code&gt;*.d.ts&lt;/code&gt; file or some compiled JavaScript code. And on top of it all it makes publishing packages way faster than ever before.&lt;/p&gt;
&lt;p&gt;How did it achieve that? Let&#39;s take a step back and assess the current situation of packaging JavaScript code in 2024.&lt;/p&gt;
&lt;h2&gt;Packaging for npm in 2024 is a mess&lt;/h2&gt;
&lt;p&gt;Honestly, it&#39;s a mess. There is no point in sugar-coating it. A buddy of mine wanted to publish his first every library on npm, but was quickly discouraged when he realized how much work and specialized knowledge that entails. You have to care about CommonJS vs ESM, fiddle with a bunch of settings to make &lt;code&gt;*.d.ts&lt;/code&gt; files work and the list goes on. In the end, he aborted the mission and instead just copy and pasted files between his projects around. It was much less of a hassle compared to having to deal with all the packaging woes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Packaging code should be as easy as the act of uploading files. Packaging and sharing code should be so easy that every developer, no matter their experience level, can do it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Even for experienced developers, creating npm packages that can be used in every environment is quite a challenge. Mark Erikson from the Redux team &lt;a href=&quot;https://blog.isquaredsoftware.com/2023/08/esm-modernization-lessons/&quot;&gt;wrote a blog post&lt;/a&gt; spanning multiple pages about his experience of creating npm packages. The thing is that packaging and sharing code among developers should be easy. Developers shouldn&#39;t have to read a dozen blog posts and become experts of the inner workings of all these tools to be able to share code. It should just work.&lt;/p&gt;
&lt;p&gt;Various CLI tools emerged to help with this situation, but this just adds another friction point. You got tool A which can fix a part of the problem, then you have tool B which only deals with another piece of the problem and so on. Before you know it, you have to stay on top of which tool is the right one to use, up until it will be replaced by yet another tool. It&#39;s bad.&lt;/p&gt;
&lt;p&gt;So, how do we fix all of this?&lt;/p&gt;
&lt;h2&gt;Why generating &lt;code&gt;.d.ts&lt;/code&gt; files takes so much time&lt;/h2&gt;
&lt;p&gt;At the heart of the problem is an architectural one. We only ever ship build artifacts, the compiled JS and the relevant &lt;code&gt;.d.ts&lt;/code&gt; files in a package. Shipping the original TypeScript source files would be much better, but barely any tool in the JS ecosystem works with that. The assumption that nothing inside the &lt;code&gt;node_modules&lt;/code&gt; folder needs to be compiled is too widely baked in our tools. Shipping the TypeScript sources instead of &lt;code&gt;.d.ts&lt;/code&gt; files would also be a performance regression when working with TypeScript.&lt;/p&gt;
&lt;p&gt;Parsing &lt;code&gt;.d.ts&lt;/code&gt; files is much quicker as they contain only the bits needed for type checking compared to a normal &lt;code&gt;.ts&lt;/code&gt; file. A &lt;code&gt;.d.ts&lt;/code&gt; file contains no function bodies, or other stuff, just the type definitions that are needed for consuming a module.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Input: add.ts&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SOME_NUMBER&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;quickMath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SOME_NUMBER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Output: add.d.ts, contains only the bits needed for type checking&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;quickMath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oftentimes, the return type of a function is not known. It has to be inferred by the TypeScript compiler by walking and checking the whole function body first. Inference is a very costly thing to do in terms of performance, especially for complex functions. Therefore the goal of creating &lt;code&gt;.d.ts&lt;/code&gt; files is to get rid of all inference, so that the TypeScript compiler only needs to read these files and do no additional work. Type inference is the main reason creating those &lt;code&gt;.d.ts&lt;/code&gt; files takes so long. And because this process takes so long, we try to do as much of it ahead of time, when creating the npm package.&lt;/p&gt;
&lt;p&gt;All this changes with isolated declarations.&lt;/p&gt;
&lt;h2&gt;Entering a new era of packaging&lt;/h2&gt;
&lt;p&gt;The idea behind isolated declarations is that you make it trivial for tools other than the TypeScript compiler to generate those &lt;code&gt;.d.ts&lt;/code&gt; files from TypeScript source code. It does it by requiring explicit return types for exported functions and other things. But don&#39;t worry, if a type is complex to reason about, the TypeScript language server comes with a handy feature to infer the missing type for you.&lt;/p&gt;
&lt;p&gt;By turning the process into a mere syntax stripping one, the time to create &lt;code&gt;.d.ts&lt;/code&gt; is very close to 0s. With a specialized Rust-based parser this happens in the blink of an eye even for gigantic projects as that work can now be parallized. It&#39;s so fast that you don&#39;t even notice it. Without isolated declarations you always &lt;em&gt;had&lt;/em&gt; to invoke the &lt;code&gt;tsc&lt;/code&gt; compiler and run a full type checking pass.&lt;/p&gt;
&lt;p&gt;Since the cost to generate definition files became next to nothing, it&#39;s easily fast enough to do on the fly when needed. We can flip the process of publishing on its head: Instead of dealing with all the hassle of building &lt;code&gt;.d.ts&lt;/code&gt; files and publishing them to npm ahead of time, you can just upload your TypeScript source code as is, and the definition files are automatically generated on the fly whenever you install a package. Generation of the &lt;code&gt;.d.ts&lt;/code&gt; doesn&#39;t occur when publishing a package, it happens when you install a package.&lt;/p&gt;
&lt;p&gt;This also changes how you declare the available entries for your package. Previously, with npm you had to reference a couple of build artifacts to make this work.&lt;/p&gt;
&lt;pre class=&quot;language-jsonp&quot;&gt;&lt;code class=&quot;language-jsonp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// package.json&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@my-scope/my-package&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;module&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;exports&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token property&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token property&quot;&gt;&quot;types&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./dist/types/index.d.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token property&quot;&gt;&quot;import&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./dist/index.js&quot;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token property&quot;&gt;&quot;./foo&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token property&quot;&gt;&quot;types&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./dist/types/foo.d.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token property&quot;&gt;&quot;import&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./dist/foo.js&quot;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a system based on isolated declarations this is much simpler: Just reference your source files directly.&lt;/p&gt;
&lt;pre class=&quot;language-jsonp&quot;&gt;&lt;code class=&quot;language-jsonp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// jsr.json&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;@my-scope/my-package&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;exports&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token property&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./src/index.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token property&quot;&gt;&quot;./foo&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./src/foo.ts&quot;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pointing the entries of your package to the actual source files feels much more natural as it frees you from worrying about packaging artifacts.&lt;/p&gt;
&lt;h2&gt;Using it in production&lt;/h2&gt;
&lt;p&gt;Now of course, talking about things in theory is all good and well, but how does such a thing feel in production? The core idea behind isolated declarations have been floated around various times in the TypeScript issue tracker, but it took some time and many smart minds to finalize. Here at Deno (disclaimer: I work for Deno) we&#39;ve been following that process from beginning very closely and starting working on our own rust-based version of that. The system that we&#39;ve been talking about so far in this post, is exactly how packaging in Deno works since early December 2023. It&#39;s been used in production by every Deno user for more than half a year by now.&lt;/p&gt;
&lt;p&gt;Things like &amp;quot;go to source&amp;quot; in your editor work out of the box and actually move you to the source code rather than some random definition file.&lt;/p&gt;
&lt;div style=&quot;margin: 0 auto; max-width: 860px&quot;&gt;
&lt;video controls=&quot;&quot; loop=&quot;&quot;&gt;
  &lt;source src=&quot;https://marvinh.dev/media/jsr-go-to-source.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;/div&gt;
&lt;p&gt;We realized the usefulness of having such a system and made it work for everyone, not just for Deno users too. You can use it with &lt;code&gt;npm&lt;/code&gt;, with &lt;code&gt;yarn&lt;/code&gt;, with &lt;code&gt;pnpm&lt;/code&gt; or even with &lt;code&gt;bun&lt;/code&gt;. The &lt;a href=&quot;https://jsr.io/&quot;&gt;JSR&lt;/a&gt; registry does this by implementing the npm protocol for installing packages and simply acts a yet another npm registry when talking with these package managers. From the package manager&#39;s point of view it&#39;s no different than talking to any other private npm registry that you may be already using in your company.&lt;/p&gt;
&lt;p&gt;Since npm clients don&#39;t have a system in place for generating definition files on the fly, we generate them for the npm tarballs during the publishing process behind the scenes. Therefore the npm tarball comes with the standard transpiled &lt;code&gt;.js&lt;/code&gt; files like you&#39;re already used to from other projects. The whole npm ecosystem is based around shipping &lt;code&gt;.js&lt;/code&gt; files and changing that would break the it. The takeaway here is not to only publish TypeScript to npm.&lt;/p&gt;
&lt;p&gt;But if you &lt;em&gt;do&lt;/em&gt; use a runtime that &lt;em&gt;can&lt;/em&gt; run TypeScript natively &lt;em&gt;and&lt;/em&gt; use a registry that can also distribute the original TypeScript sources, then having isolated declarations is a game changer.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Isolated declarations just changed the game of publishing forever. It made it a near zero cost process to generate definition files, which opens the door for generating definition files on the fly. This greatly simplifies the publishing process to a mere &lt;em&gt;upload&lt;/em&gt; of source files, which is much quicker to do. Right now, &lt;a href=&quot;https://jsr.io/&quot;&gt;JSR&lt;/a&gt; is the first and only registry to take advantage of that and it can be used with any package manager and in any runtime. In a nutshell, JSR is aimed at everyone who feels like life is too short to deal with all packaging woes and just wants to share code. Try out it out and &lt;a href=&quot;https://jsr.io/&quot;&gt;see for yourself&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Speeding up the JavaScript ecosystem - Server Side JSX</title>
    <link href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-9/"/>
    <updated>2024-05-15T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-9/</id>
    <content type="html">&lt;p&gt;In the realm of web development, the efficiency of server-side rendering HTML plays a crucial role in delivering fast and responsive user experiences. However, a notable challenge arises from the existing JSX transforms that turn JSX into valid JavaScript: They are primarily tailored for browser environments, often generating excessive memory overhead and causing frequent Garbage Collection pauses. This overhead can be nearly eliminated by rethinking how we transform JSX on the server.&lt;/p&gt;
&lt;h2&gt;Optimizing JSX for the server&lt;/h2&gt;
&lt;p&gt;When looking at the transpiled output of commonly used JSX transforms, it’s easy to see why it’s causing frequent GC pauses. They typically don’t differentiate between static and dynamic parts of a JSX &amp;quot;block&amp;quot; and allocate many short-lived objects for both an element and their attributes. The amount of objects to be allocated grows very quickly, the more JSX elements you have.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// input&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;heading&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;hello world&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// output: &quot;classic&quot; createElement transform&lt;/span&gt;&lt;br /&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;h1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;heading&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hello world&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// output: newer &quot;automatic runtime&quot; transform&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; jsx &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; _jsx &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react/runtime&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;_jsx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;h1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;heading&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hello world&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the server, neither of those transformation outputs are ideal. They both create many short-lived objects that will be immediately thrown away after rendering. Since the input JSX element is completely static, the optimal solution would be to transform it directly to a plain string which bypasses the need for those temporary objects in the first place.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// input&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;heading&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;hello world&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// output: precompiled HTML string&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; html &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;h1 id=&quot;heading&quot;&gt;hello world&amp;lt;/h1&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Allocating one string is much less taxing on the Garbage Collector than creating and cleaning up a dozen objects, regardless of how big or small those objects might be.&lt;/p&gt;
&lt;h2&gt;Mixing static and dynamic parts&lt;/h2&gt;
&lt;p&gt;But not all templates are static and a huge part of rendering involves mixing static and dynamic pieces. We have something for that already built-in in JavaScript: Tagged template literals. They differentiate between static and dynamic inputs already.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// input&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;heading&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;My name is &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// As a tagged template string&lt;/span&gt;&lt;br /&gt;jsxTemplate&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;h1 id=&quot;heading&quot;&gt;My name is &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/h1&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From an implementation point of view, tagged templates share the following function signature, which is perfect for what we want to do:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;jsxTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;statics&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;expressions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;unknown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can tailor our JSX transform to generate an array of the static parts of each JSX block and pass the dynamic parts as additional arguments.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; template &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;h1 id=&quot;heading&quot;&gt;My name is&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;/h1&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;jsxTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;template&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’ve benchmarked if it would make a difference between passing all expressions as an array, or using a rest parameter, but couldn’t find any meaningful changes in performance.&lt;/p&gt;
&lt;h2&gt;Making it secure&lt;/h2&gt;
&lt;p&gt;A crucial aspect of generating HTML is to ensure that dynamic input is properly escaped and cannot lead to XSS vulnerabilities. This is something we should take care of in our JSX transform automatically. By wrapping every dynamic part with an additional function call that escapes passed content, we can remove those risks entirely.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; template &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// The `name` variable is dynamic and we’ll wrap it with&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// an escape function automatically&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;jsxTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;template&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;jsxEscape&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The benefit of baking this into the JSX transform is that it requires frameworks to supply an implementation for &lt;code&gt;jsxEscape&lt;/code&gt; which reduces the risk of forgetting to escape things properly. Whilst we could leverage the same function for dynamic attribute values as well, most frameworks handle attribute values differently based on which attribute they are dealing with. A common thing during server-side rendering is to drop all event related props like &lt;code&gt;onClick&lt;/code&gt; for example.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// input&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;someVariable&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;hello world&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// output: precompiled&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; template &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;h1 &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&gt;hello world&amp;lt;/h1&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;jsxTemplate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;template&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;jsxAttr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;class&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the &lt;code&gt;jsxAttr&lt;/code&gt; function framework authors can decide to drop the whole attribute if desired. Note that they still need to ensure that they’re escaping the attribute value accordingly.&lt;/p&gt;
&lt;h2&gt;What about components?&lt;/h2&gt;
&lt;p&gt;Components are a bit of a special case in that it’s entirely up to the framework how they’re implemented and what they do under the hood. If we want to keep our JSX transform usable for any framework, we cannot make assumptions here. For that reason we’ll simply fall back to the automatic runtime transform in this case.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// input&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// output: precompiled, same as automatic runtime transform&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;jsx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Foo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;How much faster is it in the real world?&lt;/h2&gt;
&lt;p&gt;Synthetic benchmarks show that a precompiled approoach is around 7-20x faster, but the more interesting question is how it behaves outside of that. Real projects are often very different from synthetic benchmarks. For that reason I picked my own site for this. The code has a healthy mix of static parts and dynamic components. As usual, the numbers are measured on my MacBook M1 Air.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Transform&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Time&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;ops/s&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;improvement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Classic&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;40.92 µs/iter&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;24,439.7&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;1x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automatic Runtime&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;38.32 µs/iter&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;26,098.1&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;1.06x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Precompile&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;5.12 µs/iter&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;195,312.5&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;8x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Visualized as a chart:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/jsx-precompile-chart.png&quot; alt=&quot;Precompile is 8x faster&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Interestingly, the core idea presented here is nothing new. In truth, this is how every templating language that outputs HTML has worked since at least the early 2000s (likely even older). However, with the rise of JSX and the popularity of Single-Page-Apps, focus was shifted away from the server to the browser. Since then many framework specific transforms or even full custom file-formats have been invented. There is Svelte, there is Vue and there is Solid which works closest like the strategy described in this post. They also transpile JSX to a stringified output.&lt;/p&gt;
&lt;p&gt;The key difference here though is, that the transform isn’t tied to a particular framework. It works like the standard &amp;quot;classic&amp;quot; and &amp;quot;automatic runtime&amp;quot; JSX transforms in that it can be used with any JSX-based framework. Whether you are using Preact, Hono’s JSX layer, or something entirely different or custom grown is not important as you can support the &amp;quot;precompile&amp;quot; transform with minimal effort. Both Preact and Hono’s JSX support it out of the box already.&lt;/p&gt;
&lt;p&gt;Enabling the new &amp;quot;precompile&amp;quot; transform in &lt;a href=&quot;https://deno.com/&quot;&gt;Deno&lt;/a&gt; &lt;em&gt;(Disclaimer: I&#39;m employed by Deno)&lt;/em&gt; is as easy as changing one line in the config file.&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; // deno.json&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; {&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &quot;imports&quot;: {&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &quot;preact&quot;: &quot;npm:preact@^10.22.0&quot;,&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &quot;preact-render-to-string&quot;: &quot;npm:preact-render-to-string@^6.4.2&quot;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   },&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &quot;compilerOptions&quot;: {&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;     &quot;jsx&quot;: &quot;react-jsx&quot;,&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;     &quot;jsx&quot;: &quot;precompile&quot;,&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &quot;jsxImportSource&quot;: &quot;preact&quot;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   }&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; }&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And with that change you can make rendering JSX on the server in your app 7-20x faster and reduce Garbage Collection times by 50%. The more static elements you have the faster the transform can render HTML. The beauty about this transform is that even in the worst case scenario where there is no static content, it will have at least the same performance characteristics as the existing JSX transforms. It will never be slower, it can only be faster.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Speeding up the JavaScript ecosystem - Tailwind CSS</title>
    <link href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-8/"/>
    <updated>2023-11-01T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-8/</id>
    <content type="html">&lt;p&gt;Admittedly, I currently don’t have a bigger project written with Tailwind CSS at hand at the moment. Those that do use Tailwind are too small in scope to make a meaningful performance analysis. So I thought what better way than to profile Tailwind on its very own tailwindcss.com site! Right at the start I ran into a problem though: The project is built with Next.js which makes it very difficult to obtain meaningful traces off. What’s more is that the traces contained too much noise completely unrelated to TailwindCSS.&lt;/p&gt;
&lt;p&gt;Instead, I decided to run the Tailwind CLI over the project with the very same config to obtain some performance traces. Running the CLI build takes a total of 3.2s, whereas at runtime 1.4s were spent in Tailwind. The machine the numbers are pulled from is my personal MacBook M1 Air. Looking at the profile we can make out some key areas where time is spent:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-tailwind-profile.jpg&quot; alt=&quot;Profile of running Tailwind CSS. A good portion of the time is spent with PostCSS parsing and class name extraction&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As usual in my posts, the x-axis of the flame graph doesn’t show time “when it happened” but rather the accumulated time of each call stack which are merged together here. This makes it a lot easier to see problem areas at a glance. I’m using &lt;a href=&quot;https://www.speedscope.app/&quot;&gt;SpeedScope&lt;/a&gt; to visualize the CPU profile.&lt;/p&gt;
&lt;p&gt;There is a block that deals with extracting potential candidates for parsing, a block for config and plugin initialization, CSS generation, some PostCSS stuff and when there is PostCSS then autoprefixer typically can be mentioned in the same breath as the two are very frequently used together. It&#39;s noticeable that loading autoprefixer without doing anything seems to consume quite a bit of time already.&lt;/p&gt;
&lt;h2&gt;Switching things up&lt;/h2&gt;
&lt;p&gt;Going through the Tailwind CSS code base and looking at the profile, there definitely some places where functions could be optimized more. But if we did that we’d only get a couple of single digit percentage improvements. In the previous posts there was typically something obvious jumping out in the profiles, but what do we do here where there is no single obvious indication where time is spent?&lt;/p&gt;
&lt;p&gt;The secret to achieving speedups of multiple factors, not just low percentages, is less about applying generic rules or habits like “Don’t create closures inside for-loops”. It’s a common misconception that if you follow all these “best practices” that your code will be fast, because the uncomfortable truth in most instances (read not all) is that it won’t matter much. What makes code truly fast is being aware of what it’s supposed to solve and then taking the shortest path to achieve that goal.&lt;/p&gt;
&lt;p&gt;So as a challenge, I thought it would be fun to look at what the architecture of Tailwind’s code would look like, if we built it from scratch with performance in mind. Would we make different decisions? But to be able to find an optimal architecture, we need to know which problem Tailwind solves and think of the shortest path to achieve that.&lt;/p&gt;
&lt;p&gt;Little spoiler:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-tailwind-final.jpg&quot; alt=&quot;Screenshot of a profile of a custom tailwind parser&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;How Tailwind CSS works&lt;/h2&gt;
&lt;p&gt;At its core, the way Tailwind CSS works is that you pass it some CSS files and it looks for &lt;code&gt;@tailwind&lt;/code&gt; rules inside of them. If it encounters such a rule it will crawl the other files in your project to look for tailwind class names and inject it in place in the CSS file where the &lt;code&gt;@tailwind&lt;/code&gt; rule was found. There are some more aspects to it, but for sake of simplicity for this article, we’ll ignore the other at-rules for now.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Input */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; base&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; components&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@tailwind&lt;/span&gt; utilities&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.foo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;…is transformed to:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.border&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;border-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.border-2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;border-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;/* …etc */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.foo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Based on that we can identify several stages in the inner workings of Tailwind CSS:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Scan &lt;code&gt;.css&lt;/code&gt; files for &lt;code&gt;@tailwind&lt;/code&gt; rules&lt;/li&gt;
&lt;li&gt;Find all files to extract tailwind class names from based on glob patterns provided by the user in the tailwind configuration&lt;/li&gt;
&lt;li&gt;Once those files have been found, extract potential tailwind class names&lt;/li&gt;
&lt;li&gt;Parse potential tailwind class names to check if they are really a tailwind class name. If they are, generate some CSS from that&lt;/li&gt;
&lt;li&gt;Replace the &lt;code&gt;@tailwind&lt;/code&gt; rule in the original css file with the generated CSS&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Optimizing the extraction stage&lt;/h2&gt;
&lt;p&gt;Since there are only three valid &lt;code&gt;@tailwind&lt;/code&gt; rule values, we can bypass the whole PostCSS parsing step by using a basic regex:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;@tailwind&#92;s+(base|components|utilities)(?:;|$)&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;gm&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this regex, finding the &lt;code&gt;@tailwind&lt;/code&gt; rules and their position in all the CSS files is essentially free, as it takes about &lt;code&gt;0.02ms&lt;/code&gt;. That time hardly matters compared to the full 3.2s time it took for Tailwind CSS. When it comes to finding all files based on user specified glob pattern, there isn’t much we can do that would affect the total time, as we need to reach out to the file system anyways and we’re limited by the &lt;code&gt;readFile&lt;/code&gt; function the runtime provides us.&lt;/p&gt;
&lt;p&gt;However, once those files are read and we need to extract potential tailwind class name candidates, there is a lot we can do. There is a problem though: How do we detect what is a tailwind class name and what is not? It might sound simple on the surface, but it’s actually not that easy. The problem is that there is no maker or any other indication that a sequence of characters is a valid tailwind class name. There can be combinations of words which have the same format as a tailwind class name, but don’t exist.&lt;/p&gt;
&lt;p&gt;Examples of valid tailwind class names:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ml-2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;border-b-green-500&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dark:text-slate-100&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dark:text-slate-100/50&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[&amp;amp;:not(:focus-visible)]:focus:outline-none&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Is &lt;code&gt;foo-bar&lt;/code&gt; a valid tailwind class name? It’s not part of the default tailwind grammar, but it could’ve been added by the user. So the only real option we have here is to reduce the search space as best as we can and then feed our parser with the remaining candidates. If the parser generated some CSS, then we know that the class name was valid. If it didn’t then it wasn’t valid. This in turn means that we need to optimize our parser to bail out as quickly as possible if it detects a string value that it has no definition for.&lt;/p&gt;
&lt;p&gt;To remind ourselves: This takes about &lt;code&gt;388ms&lt;/code&gt; in Tailwind CSS currently.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-tailwind-extract.jpg&quot; alt=&quot;Zoomed in screenshot of the profile showing 388.63ms being spent on extracting tailwind class names&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I’ve patched Tailwind CSS locally to reveal some stats about the values the extractor pulls out.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Parsed files: 454&lt;/li&gt;
&lt;li&gt;Candidate strings: 26466&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But what’s much more interesting is looking at the most common top 10 values that the extraction code pulled out:&lt;/p&gt;
&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;- 9774x &#39;&#39;&lt;br /&gt;- 2634x &amp;lt;/div&gt;&lt;br /&gt;- 1858x }&lt;br /&gt;- 1692x ```&lt;br /&gt;- 1065x },&lt;br /&gt;-  820x ---&lt;br /&gt;-  694x ```html&lt;br /&gt;-  385x {&lt;br /&gt;-  363x &gt;&lt;br /&gt;-  345x &amp;lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In other words: Out of the 26466 matched strings, 19630 of them are obviously invalid tailwind class names. Now to be fair, Tailwind CSS has some caching in place to alleviate checking if something is a false positive or not. And there already is a code comment that says that any improvement to their regex could speed up Tailwind CSS by as much as 30%.&lt;/p&gt;
&lt;h3&gt;Regexing all the things&lt;/h3&gt;
&lt;p&gt;The blessing and curse at the same time of using regex here is that it isn’t language aware. It doesn’t know if we’re operating on &lt;code&gt;.js&lt;/code&gt; or &lt;code&gt;.html&lt;/code&gt; files and what’s worse is that languages can be embedded into each other. An &lt;code&gt;.html&lt;/code&gt; file can host HTML, JavaScript and CSS at the same time. Same is true for JSX in &lt;code&gt;.jsx&lt;/code&gt; files. When it comes to JavaScript code, then we can assume that we only need to look at strings.&lt;/p&gt;
&lt;p&gt;A quick and dirty regex later, we reduced the search space from &lt;code&gt;26466&lt;/code&gt; down to &lt;code&gt;9633&lt;/code&gt; candidates. Still not optimal, but a lot better than what we started with. A lot of the extracted strings now resemble much more potential tailwind candidates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;relative not-prose [a:not(:first-child)&amp;gt;&amp;amp;]:mt-12&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;none&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;break-after&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;grid-template-rows&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each extracted string holds one or more potential candidates. We can reduce the search space further by firing another regex on each extracted string to pull out the parts that could be a valid tailwind class name. Luckily for us, the grammar of a valid tailwind class name follows reasonably simple rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No spaces allowed&lt;/li&gt;
&lt;li&gt;Variants must end with a colon &lt;code&gt;:&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Arbitrary values are defined with wrapping brackets &lt;code&gt;[foo]&lt;/code&gt;. They must be located at the end of the class name&lt;/li&gt;
&lt;li&gt;A variant can also be arbitrary: &lt;code&gt;[&amp;amp;&amp;gt;.foo]:border-2&lt;/code&gt;. Must still not contain spaces&lt;/li&gt;
&lt;li&gt;Anything other than the values inside the brackets must only contain numbers, alphabetic characters or the minus sign. I’m not sure if underscore is allowed to, but I guess it could be a user defined tailwind class name&lt;/li&gt;
&lt;li&gt;A valid Tailwind class name must start with &lt;code&gt;[&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt;, &lt;code&gt;a-z&lt;/code&gt; or &lt;code&gt;0-9&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All this matching does cost some time though and increases the total extraction time to &lt;code&gt;92ms&lt;/code&gt;. And after all our efforts of reducing the search space, we’re still left with around 8000 potential tailwind class names (remember, previously extracted strings could hold multiple candidates).&lt;/p&gt;
&lt;p&gt;So far we achieved pretty commendable gains. We reduced the extraction time from Tailwind’s original &lt;code&gt;388ms&lt;/code&gt; down to &lt;code&gt;98ms&lt;/code&gt;. That’s roughly a 4x improvement.&lt;/p&gt;
&lt;h2&gt;Turning class names into CSS&lt;/h2&gt;
&lt;p&gt;At this stage we still haven’t generated any CSS rules yet. We still need some rules to replace in favor of the &lt;code&gt;@tailwindcss&lt;/code&gt; rules in the original CSS file where we started from. But we are now in a position to do just that with the list of potential tailwind class names. A lot of these are likely false positives so we need to ensure we can bail out as quickly as possible if we detect that a class name doesn’t render CSS.&lt;/p&gt;
&lt;p&gt;The first step is parsing the preceding variants if there are any. Remember that variants can be detected by a trailing colon &lt;code&gt;:&lt;/code&gt; character. One key aspect of variants is that they only influence the selector and maybe the surrounding media query, if present. They are not used to generate CSS properties themselves. Parsing the variants is a bit of grunt work and nothing really special. If we detect that a supposed variant doesn’t exist we can exit early already.&lt;/p&gt;
&lt;p&gt;What’s more interesting than variants is the rule generation aspect. The majority of tailwind class names don’t feature a variant. Since tailwind mirrors a lot of CSS properties, the amount of potential matches we need to make is quite large. I’ve tried various approaches like matching all static tailwind class names up front, putting everything in an object with methods to use like a virtual function table and more. But in the end the fastest and I felt like the easiest to maintain was one giant dumb switch statement.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;lexer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; hasNegativePrefix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; first &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lexer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;nextSegment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;first&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;“aspect”&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;“block”&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;lexer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEnd&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// bail out&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;display: block&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;“inline”&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lexer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isEnd&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;display: inline&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; second &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lexer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;nextSegment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;        second &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; “block” &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; second &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; “flex” &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; second &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; “table”&lt;br /&gt;        &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; second &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; “grid”&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// bail out&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;display: inline-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;second&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// ...1000 lines more of this&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This might look like pretty standard parser code, but there are some interesting aspects. The obvious one is that at every step we check if we’re still on a valid path. This adds a lot of additional checks, but I found that the cost for these is offset by the gains of being able to bail out earlier. In some prior iterations I made a mistake in the extracting portion and ended up feeding way too many known false positive strings to this parse function. But because the parse function quickly bails out on invalid class names it took me a while to notice as it was still fast overall.&lt;/p&gt;
&lt;p&gt;Of note is also the &lt;code&gt;hasNegativePrefix&lt;/code&gt; argument that’s passed to the &lt;code&gt;parse()&lt;/code&gt; function. Many numeric based properties like padding can receive negative values by prefixing the class name with a minus &lt;code&gt;-&lt;/code&gt; character.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pl-2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// -&gt; padding-left: 0.5rem;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token string&quot;&gt;&quot;-pl-2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// -&gt; padding-left: -0.5rem;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The leading minus character is stripped before passing it to the &lt;code&gt;parse()&lt;/code&gt; function, so that we can reuse the same case branches for both the normal and the negative case. It’s not shown here, but the parser also supports arbitrary values, the important declaration, color values with opacity and more.&lt;/p&gt;
&lt;p&gt;Although I didn’t implement every single rule, all the syntax variations are supported. I did implement a fair share of the rules though, about 126 of them. That’s roughly 80% of the tailwind grammar. Even though this is mostly a prototype I wanted to get a better picture of how the parser would scale.&lt;/p&gt;
&lt;p&gt;With the generated rules in hand we can now finally replace the &lt;code&gt;@tailwind&lt;/code&gt; rule in the original CSS file. If we want it to be source map aware we can use &lt;a href=&quot;https://www.npmjs.com/package/magic-string&quot;&gt;Magic String&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With everything in place, here are the final measurements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extract: &lt;code&gt;98ms&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Parse: &lt;code&gt;21ms&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Total time: &lt;code&gt;192ms&lt;/code&gt; (including runtime startup time)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The whole project consists of 5 files (excluding tests) and is just shy of 3000 lines of code.&lt;/p&gt;
&lt;video controls=&quot;&quot; loop=&quot;&quot;&gt;
  &lt;source src=&quot;https://marvinh.dev/media/js-tools-tailwind-custom.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;h2&gt;What about Rust?&lt;/h2&gt;
&lt;p&gt;The reason our little project here is faster than the og Tailwind CSS cli is that we sidestepped parsing anything with PostCSS completely and focuses on generating CSS rules as fast as possible. The Tailwind team is currently in the process of rewriting Tailwind CSS in Rust and from what I can gather they are already far along. I don’t have any numbers for that as it’s not yet released. What remains to be solved like with any JavaScript tool that is being rewritten to rust is what their plugin story will look like. Tailwind does support custom variants or full rules being defined in their config. Once it’s out it will be interesting to compare the two.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This was a fun little exploration to see what the architecture of Tailwind CSS tuned for performance would look like. Admittedly, this one took me way longer to write than previous articles because of the amount of prototyping involved to arrive at something I was satisfied with. Whatever the Tailwind CSS team decides to do, I’m really looking forward to that.&lt;/p&gt;
&lt;p&gt;To me, Tailwind CSS is the jQuery of CSS. Not everyone likes it, but the positive effect it had on the web industry is undeniable. It enables a whole new generation of developers to get into web development.&lt;/p&gt;
&lt;p&gt;I really appreciate what they’re trying to do, because it mirrors my own path to becoming a developer. When I started with web development jQuery was at its peak and without it I would have never touched JavaScript. It wasn’t until 2 years into my career that I took an interest in JavaScript itself and learned the fundamentals. Tailwind CSS is doing exactly that for today’s developers when it comes to CSS.&lt;/p&gt;
&lt;p&gt;I’m really glad that they exist, even if their compiler could be faster.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Speeding up the JavaScript ecosystem - The barrel file debacle</title>
    <link href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-7/"/>
    <updated>2023-10-08T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-7/</id>
    <content type="html">&lt;p&gt;Let&#39;s imagine you are working on a big project with many files. You add a new file to work on a new feature and import a function from another directory into your code.&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; foo &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./some/other-file&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;myCoolCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Pretend that this is super smart code :)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Excited about finishing your feature, you run the code and realize that it takes an awfully long time to complete. The code you&#39;ve written is pretty straight forward and shouldn&#39;t consume that much time. Concerned by this, you sprinkle in some measuring code to see how long your function took to do its thing.&lt;/p&gt;
&lt;pre class=&quot;language-tsx&quot;&gt;&lt;code class=&quot;language-tsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; foo &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./some/other-file&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;myCoolCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;timeEnd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You run the code again, and to your surprise the measurements you inserted show that it&#39;s blazing fast™. You repeat the measuring steps, but this time insert the &lt;code&gt;console.time()&lt;/code&gt; statements in the main entry file of your project and run the code again. But no dice, the logged measurements just confirm that your code itself is super fast. What is happening?&lt;/p&gt;
&lt;p&gt;Well, strap in. This is the story of the devastating effects barrel files have on your code.&lt;/p&gt;
&lt;h2&gt;Gathering more information&lt;/h2&gt;
&lt;p&gt;The key piece of information we have obtained so far is that the runtime of the code isn&#39;t the issue. You measured it, and it was a fraction of the total time. This means we can assume that all the other time is wasted either &lt;em&gt;before&lt;/em&gt; or &lt;em&gt;after&lt;/em&gt; running our code. From experience when it comes to tooling, the time is usually spent before running the project code.&lt;/p&gt;
&lt;p&gt;You got an idea: You remember hearing that some npm packages pre-bundle their code for performance reasons. Maybe this could help here? You decide to test that theory and bundle your code with &lt;a href=&quot;https://esbuild.github.io/&quot;&gt;esbuild&lt;/a&gt; into a single file. You purposely disable any forms of minifications, because you want your code to be as close as possible to the original source.&lt;/p&gt;
&lt;p&gt;Upon completion, you then run the bundled file to repeat the experiment and voilá, it finishes in a blink of an eye. Out of curiosity, you measure the time it takes to run esbuild &lt;em&gt;and&lt;/em&gt; run the bundled file together and notice that both of them combined are still quicker than running the original source code. Huh? What is going on?&lt;/p&gt;
&lt;p&gt;Then it hits you: The main thing a bundler does is flatten and merge the module graph. What was once composed of thousands of files, is merged into a single file thanks to esbuild. This would be a strong indicator that the size of the module graph is the true problem here. And barrel files are the main cause of that.&lt;/p&gt;
&lt;h2&gt;Anatomy of a barrel file&lt;/h2&gt;
&lt;p&gt;Barrel files are files that only export other files and contain no code themselves. As a non-native speaker that term is confusing to me, but let&#39;s roll with it. In the days before editors had auto imports and other niceties, many developers tried to keep the number of import statements they had to write by hand to a minimum.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Look at all these imports&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; foo &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; bar &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; baz &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../baz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gave rise to a pattern where every folder got its own &lt;code&gt;index.js&lt;/code&gt; file that merely re-exported code from other files in usually the same directory. In a way this amortized the manual typing work because once such a file is in place, all the other code only needs to reference one import statement.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// feature/index.js&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./baz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The previously shown import statements can now be collapsed into a single line.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; foo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bar&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; baz &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../feature&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After a while, this pattern spreads throughout the codebase and every folder in your project has an &lt;code&gt;index.js&lt;/code&gt; file. Kinda neat, isn&#39;t it? Well, no.&lt;/p&gt;
&lt;h2&gt;Everything is not fine&lt;/h2&gt;
&lt;p&gt;In such a setup a module very likely imports another barrel file which pulls in a bunch of other files, which then imports yet another barrel file and so on. Ultimately, you typically end up importing every single file in your project through a spiderweb of import statements. And the bigger the project, the longer it takes to load all of these modules.&lt;/p&gt;
&lt;p&gt;Ask yourself: What&#39;s quicker? Having to load 30k files or just 10? Probably only loading 10 files is faster.&lt;/p&gt;
&lt;p&gt;It&#39;s a common misconception among JavaScript developers that modules would only be loaded when needed. This is not true, because doing so would break code relying on globals or module execution order.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// a.js&lt;/span&gt;&lt;br /&gt;globalThis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// b.js&lt;/span&gt;&lt;br /&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;globalThis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// should log: 123&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// index.js&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./a&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./b&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the engine wouldn&#39;t load the first &lt;code&gt;./a&lt;/code&gt; import, then the code would unexpectedly log &lt;code&gt;undefined&lt;/code&gt; instead of &lt;code&gt;123&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Effects of barrel files on performance&lt;/h2&gt;
&lt;p&gt;It gets worse when you take tools like test runners in consideration. In the popular jest test runner, each test file is executed in its own child process. In effect, this means that every single test file constructs the module graph from scratch and has to pay for that cost. If constructing the module graph in a project takes 6s and you have - let&#39;s say - only 100 test files, then you waste 10 minutes in total to repeatedly construct the module graph. No test or any other code is being run during that time. It&#39;s just the time the engine needs to prepare the source code so that it can then be run.&lt;/p&gt;
&lt;p&gt;Another area where barrel files gravely affect performance is any sort of import cycle linting rules. Typically linters are run on a file-by-file basis, which means the cost of constructing the module graph needs to be paid for every single file. It&#39;s not uncommon that this alone causes linting times to spiral out of control and suddenly linting takes a couple of hours in a bigger project.&lt;/p&gt;
&lt;p&gt;To get some raw numbers, I&#39;ve generated a project with files importing each other to get a better sense of the cost of constructing the module graph. Every file is empty and contains no code other than import statements. Timings are measured on my MacBook M1 Air (2020).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-module-cost.png&quot; alt=&quot;Loading 500 empty modules takes 0.15s, 1000 take 0.31s, 10000 take 3.12s, 25000 take 16.81s, 50000 take 48.44s&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see, loading fewer modules is very much worth it. Let&#39;s apply these numbers to a project with 100 test files where a test runner is used that spawns a new child process for each test file. Let&#39;s be generous here and say that our test runner can run 4 tests in parallel:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;500 modules: &lt;code&gt;0.15s * 100 / 4&lt;/code&gt; = &lt;code&gt;3.75s&lt;/code&gt; overhead&lt;/li&gt;
&lt;li&gt;1000 modules: &lt;code&gt;0.31s * 100 / 4&lt;/code&gt; = &lt;code&gt;7.75s&lt;/code&gt; overhead&lt;/li&gt;
&lt;li&gt;10000 modules: &lt;code&gt;3.12s * 100 / 4&lt;/code&gt; = &lt;code&gt;1:18m&lt;/code&gt; overhead&lt;/li&gt;
&lt;li&gt;25000 modules: &lt;code&gt;16.81s * 100 / 4&lt;/code&gt; = &lt;code&gt;~7:00m&lt;/code&gt; overhead&lt;/li&gt;
&lt;li&gt;50000 modules: &lt;code&gt;48.44s * 100 / 4&lt;/code&gt; = &lt;code&gt;~20:00m&lt;/code&gt; overhead&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since this is a synthetic setup, these are lowball numbers. In a real project these numbers are likely worse. Barrel files are not good when it comes to tooling performance.&lt;/p&gt;
&lt;h2&gt;What to do&lt;/h2&gt;
&lt;p&gt;Having only a handful of barrel files in your code is usually fine, but it gets problematic when every single folder has one. It&#39;s unfortunately not a rare occurrence in the JavaScript industry.&lt;/p&gt;
&lt;p&gt;So if you work on a project which uses barrel files extensively, there is a free optimization you can apply that makes many tasks 60-80% faster:&lt;/p&gt;
&lt;p&gt;Get rid of all barrel files.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Speeding up the JavaScript ecosystem - Polyfills gone rogue</title>
    <link href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-6/"/>
    <updated>2023-09-21T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-6/</id>
    <content type="html">&lt;p&gt;In the previous posts we looked at runtime performance and I thought it would be fun to look at node modules install time instead. A lot has been already written about various algorithmic optimizations or using more performant syscalls, but why do we even have this problem in the first place? Why is every &lt;code&gt;node_modules&lt;/code&gt; folders so big? Where are all these dependencies coming from?&lt;/p&gt;
&lt;p&gt;It all started when a buddy of mine noticed something odd with the dependency tree of his project. Whenever he updated a dependency, it would pull in several new ones and with each subsequent update the total number of dependencies grew over time.&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt; ├─┬ arraybuffer.prototype.slice &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;.2&lt;br /&gt; │ └─┬ define-properties &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;.1&lt;br /&gt; │   └── define-data-property &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;.0&lt;br /&gt; ├─┬ function.prototype.name &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;.6&lt;br /&gt; │ └─┬ define-properties &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;.1&lt;br /&gt; │   └── define-data-property &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;.0&lt;br /&gt; ├─┬ globalthis &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;.3&lt;br /&gt; │ └─┬ define-properties &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;.1&lt;br /&gt; │   └── define-data-property &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;.0&lt;br /&gt; ├─┬ object.assign &lt;span class=&quot;token number&quot;&gt;4.1&lt;/span&gt;.4&lt;br /&gt; │ └─┬ define-properties &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;.1&lt;br /&gt; │   └── define-data-property &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;.0&lt;br /&gt; ├─┬ regexp.prototype.flags &lt;span class=&quot;token number&quot;&gt;1.5&lt;/span&gt;.1&lt;br /&gt; │ ├─┬ define-properties &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;.1&lt;br /&gt; │ │ └── define-data-property &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;.0&lt;br /&gt; │ └─┬ set-function-name &lt;span class=&quot;token number&quot;&gt;2.0&lt;/span&gt;.1&lt;br /&gt; │   └── define-data-property &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;.0&lt;br /&gt; ├─┬ string.prototype.trim &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;.8&lt;br /&gt; │ └─┬ define-properties &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;.1&lt;br /&gt; │   └── define-data-property &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;.0&lt;br /&gt; ├─┬ string.prototype.trimend &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;.7&lt;br /&gt; │ └─┬ define-properties &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;.1&lt;br /&gt; │   └── define-data-property &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;.0&lt;br /&gt; └─┬ string.prototype.trimstart &lt;span class=&quot;token number&quot;&gt;1.0&lt;/span&gt;.7&lt;br /&gt;   └─┬ define-properties &lt;span class=&quot;token number&quot;&gt;1.2&lt;/span&gt;.1&lt;br /&gt; 	└── define-data-property &lt;span class=&quot;token number&quot;&gt;1.1&lt;/span&gt;.0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now to be fair, there are valid reasons why a package might depend on an additional dependencies. Here, we began to notice a pattern though: The new dependencies were all polyfills for JavaScript functions that have long been supported everywhere. The &lt;code&gt;Object.defineProperties&lt;/code&gt; method for example was shipped as part of the very first public Node &lt;code&gt;0.10.0&lt;/code&gt; release dating back to 2013. Heck, even Internet Explorer 9 supported that. And yet there were numerous packages in that dependend on a polyfill for it.&lt;/p&gt;
&lt;p&gt;Among the various packages that pulled in &lt;code&gt;define-properties&lt;/code&gt; was &lt;a href=&quot;https://github.com/jsx-eslint/eslint-plugin-react/&quot;&gt;&lt;code&gt;eslint-plugin-react&lt;/code&gt;&lt;/a&gt;. It caught my eye, because it&#39;s very popular in the React ecosystem. Why does it pull in a polyfill for &lt;code&gt;Object.defineProperties&lt;/code&gt;? There is no JavaScript engine that doesn&#39;t come with it already built in.&lt;/p&gt;
&lt;h2&gt;Polyfills that don’t polyfill&lt;/h2&gt;
&lt;p&gt;Reading the source of the packages revealed something even more bizarre: The polyfill functions were imported and called directly, rather than patching missing functionality in the runtime environment. The whole point of a polyfill is to be transparent to the user’s code. It should check if the function or method to patch is available and only add it if it’s missing. When there is no need for the polyfill it should do nothing. The odd thing to me is that the functions were used directly like a function from a library.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Why is the `define` function imported directly?&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; define &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;define-properties&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// and even worse, why is called directly?&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;polyfill&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;getPolyfill&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; getPolyfill&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; implementation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token literal-property property&quot;&gt;shim&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; shim&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead they should call &lt;code&gt;Object.defineProperties&lt;/code&gt; directly. The whole point of polyfills is to patch &lt;em&gt;the environment&lt;/em&gt; not be called directly. Compare this to what a polyfill for &lt;code&gt;Object.defineProperties&lt;/code&gt; is supposed to look like:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Check if the current environment already supports&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// `Object.defineProperties`. If it does, then we do nothing.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defineProperties&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Patch in Object.defineProperties here&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The most common place where &lt;code&gt;define-properties&lt;/code&gt; was used was ironically inside other polyfills, which loaded even more polyfills. And before you ask, the &lt;code&gt;define-properties&lt;/code&gt; package relies on even more dependencies than just itself.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; keys &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;object-keys&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; defineDataProperty &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;define-data-property&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; supportsDescriptors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;has-property-descriptors&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;defineProperties&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; defineProperties&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside &lt;code&gt;eslint-plugin-react&lt;/code&gt;, that polyfill is loaded via a polyfill for &lt;code&gt;Object.entries()&lt;/code&gt; to process &lt;a href=&quot;https://github.com/jsx-eslint/eslint-plugin-react/blob/ecadb92609998520be80d64c0bd6bc5e05934aa9/configs/all.js#L4&quot;&gt;their configuration&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fromEntries &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;object.fromentries&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;- Why is this used directly?&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entries &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;object.entries&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;- Why is this used directly?&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; allRules &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;../lib/rules&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;filterRules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;rules&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; predicate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fromEntries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rules&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;entry&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;predicate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeRules &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;filterRules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;allRules&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;rule&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;rule&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;meta&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deprecated&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Doing a little bit of housekeeping&lt;/h2&gt;
&lt;p&gt;At the time of this writing installing &lt;code&gt;eslint-plugin-react&lt;/code&gt; pulls in a whopping number of 97 dependencies in total. I was curious about how much of these were polyfills and began patching them out one by one locally. After all was done, this brought down the total number of dependencies down to 15. Out of the original 97 dependencies 82 of them are not needed.&lt;/p&gt;
&lt;p&gt;Coincidentally, &lt;code&gt;eslint-plugin-import&lt;/code&gt; which is equally popular in various eslint presets, shows a similar problems. Installing that fills up your &lt;code&gt;node_modules&lt;/code&gt; folder with 87 packages. After another local cleanup pass I was able to cut down that number to just 17.&lt;/p&gt;
&lt;h2&gt;Filling up everyone&#39;s disk space&lt;/h2&gt;
&lt;p&gt;Now you might be wondering if you’re affected or not. I did a quick search and basically every widely popular eslint plugin or preset that you can think of is affected. For some reason this whole ordeal reminds me of the &lt;code&gt;is-even&lt;/code&gt;/&lt;code&gt;is-odd&lt;/code&gt; incident the industry had a while back.&lt;/p&gt;
&lt;p&gt;Having so many dependencies makes it much harder to audit the dependencies of a project. It&#39;s a waste of space too. Case in point: Deleting all eslint plugins and presets in a project alone got rid of &lt;code&gt;220&lt;/code&gt; packages.&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;pnpm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; eslint-plugin-react eslint-plugin-import eslint-import-resolver-typescript eslint-config-next eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-prettier prettier eslint-config-prettier eslint-plugin-react-hooks&lt;br /&gt;Scope: all &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt; workspace projects&lt;br /&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;                                        &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-220&lt;/span&gt; ----------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maybe we don&#39;t need that many dependencies in the first place. My mind went to this fantastic quote by the creator of the Erlang programming language:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You wanted a banana but what you got was a gorilla holding the banana and the entire jungle - Joe Armstrong&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;All I wanted was some linting rules. I didn’t want a bunch of polyfills that I don&#39;t need.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>So you want to render colors in your terminal</title>
    <link href="https://marvinh.dev/blog/terminal-colors/"/>
    <updated>2023-04-23T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/terminal-colors/</id>
    <content type="html">&lt;p&gt;If you&#39;ve been writing command line tools for other developers to use, there will come a time where the clarity of the output can be enhanced through colors. What appears to be simple on the surface gets a bit messy as soon as you dive into the details on how colors are supported in various terminal emulators. Funnily, much of this complexity is due to historical reasons which have been kept alive to this date.&lt;/p&gt;
&lt;h2&gt;Humble beginnings&lt;/h2&gt;
&lt;p&gt;In the early days of computing there were no terminal colors. Everything was rendered in either black or white. Demand grew for more complex rendering in the terminal and that&#39;s how &lt;a href=&quot;https://en.wikipedia.org/wiki/ANSI_escape_code&quot;&gt;ANSI escape codes&lt;/a&gt; were born. They represent a special set of character sequences that control cursor location, color, background color, font styling, and other options.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// This will print the string &quot;foobar&quot; in green to the terminal&lt;/span&gt;&lt;br /&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&#92;u001b[32mfoobar&#92;u001b[39m&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...which looks like this in the terminal:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/terminal-colors-foo.png&quot; alt=&quot;Screenshot of the text &amp;quot;foobar&amp;quot; being rendered in green in the terminal&quot; /&gt;&lt;/p&gt;
&lt;p&gt;There is a catch though and that is that only a total of 16 colors are supported. Nearly half of them are lighter shades of existing colors, so the true perceived amount of colors feels even more limited. Despite those limitations, they are usually enough for most apps.&lt;/p&gt;
&lt;p&gt;One cool aspect about this color palette is that pretty much every terminal emulator allows you to change the color values. This opens up the door for theming and styling the terminal to your likings. You can see a good overview of the default palette of various terminal emulators &lt;a href=&quot;https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit&quot;&gt;on Wikipedia&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The full list of supported colors is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;black&lt;/li&gt;
&lt;li&gt;white&lt;/li&gt;
&lt;li&gt;gray + light gray&lt;/li&gt;
&lt;li&gt;red + light red&lt;/li&gt;
&lt;li&gt;green + light green&lt;/li&gt;
&lt;li&gt;yellow + light yellow&lt;/li&gt;
&lt;li&gt;blue + light blue&lt;/li&gt;
&lt;li&gt;magenta + light magenta&lt;/li&gt;
&lt;li&gt;cyan + light cyan&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All these colors can be used as foreground colors or as background colors. This is how they all render in my terminal.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/terminal-colors-ansi.png&quot; alt=&quot;All 16 ANSI colors rendered in the terminal&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Supporting all the colors&lt;/h2&gt;
&lt;p&gt;Over time computers advanced, and so did the richness of colors that developers wanted to use in their terminals. This led to the addition of an enhanced 8-bit color space which supports a whopping 256 colors. Suddenly, there wasn&#39;t only green and dark green anymore. You could now display shades of green!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/terminal-colors-ansi-256.png&quot; alt=&quot;Rendering all 256 ANSI colors in the terminal&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But why stop there? Fast forward a couple of years and with the introduction of graphic cards, demand for even more colors grew. It became common for applications to render 16 or 24-bits of colors. It didn&#39;t take long for terminal emulators to follow suit. They jumped right to 24-bit colors which is often referred to as &amp;quot;true color&amp;quot; support. I&#39;ll spare you a screenshot since that would be too big for this post. Let me just say that the jump from 256 colors to 16.7 million colors is pretty big.&lt;/p&gt;
&lt;p&gt;In summary, we ended up with 4 different color spaces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;black &amp;amp; white&lt;/li&gt;
&lt;li&gt;Ansi, 16 colors&lt;/li&gt;
&lt;li&gt;Ansi 256 colors&lt;/li&gt;
&lt;li&gt;24-bit True Color&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Detecting color support&lt;/h2&gt;
&lt;p&gt;This is the bit where it gets messy, because every terminal emulator does it slightly differently. There is no standardized way to detect which color space is supported. It&#39;s not just terminal emulators either, because today&#39;s developers expect the CI logs to be colored too. Most environments straight up don&#39;t tell you what kind of color space they support.&lt;/p&gt;
&lt;p&gt;The most common way to detect color support is by checking the &lt;code&gt;TERM&lt;/code&gt; and &lt;code&gt;COLORTERM&lt;/code&gt; environment variables. CI systems can be detected by checking for the existence of the &lt;code&gt;CI&lt;/code&gt; environment variable. Combine that with the knowledge about which operating system the program is running on, and we have a decent enough way to detect colors.&lt;/p&gt;
&lt;p&gt;If &lt;code&gt;COLORTERM&lt;/code&gt; is &lt;code&gt;24bit&lt;/code&gt; or &lt;code&gt;truecolor&lt;/code&gt;, then you know for certain that 24-bit True Colors are supported. Detecting ANSI 256 is usually done by checking if &lt;code&gt;$TERM&lt;/code&gt; ends with &lt;code&gt;256&lt;/code&gt; or &lt;code&gt;256color&lt;/code&gt;. If True Colors are supported than ANSI 256 support is a given. Same is true for the basic ANSI escape codes. Again, this detection logic is neither perfect nor elegant, but it works pretty reliable in practice. The Windows Terminal on the other hand doesn&#39;t give you anything. Both environment variables are not set there. Therfore everyone simply assumes that the terminal supports full 24-bit colors, which is the case since Windows 10 revision 14931.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;OS&lt;/th&gt;
&lt;th&gt;ANSI&lt;/th&gt;
&lt;th&gt;ANSI 256&lt;/th&gt;
&lt;th&gt;True Color&lt;/th&gt;
&lt;th&gt;$TERM&lt;/th&gt;
&lt;th&gt;$COLORTERM&lt;/th&gt;
&lt;th&gt;$CI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Terminal.app&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;code&gt;xterm-256color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iTerm&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;xterm-256color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;truecolor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows Terminal&lt;/td&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PowerShell&lt;/td&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Actions&lt;/td&gt;
&lt;td&gt;Linux (Ubuntu)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dumb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The crazy bit is that the only common terminal emulator that I came across that didn&#39;t support True Colors at the time of this writing was macOS&#39;s built in &lt;code&gt;Terminal.app&lt;/code&gt;. It only supports up to ANSI 256.&lt;/p&gt;
&lt;p&gt;CI systems are the real boss battle here, because they often advertise themselves as &lt;code&gt;dumb&lt;/code&gt; terminals with no support for colors. Since developers expect the logs to contain colors, there is no other way than to ignore both &lt;code&gt;TERM&lt;/code&gt; and &lt;code&gt;COLORTERM&lt;/code&gt; variables. Instead color support is inferred by virtue of detecting that the code runs inside the CI.&lt;/p&gt;
&lt;h2&gt;Color conversions&lt;/h2&gt;
&lt;p&gt;The missing piece when it comes to colors is converting one color space to another. When a developer uses the notation for True Colors to print something to the terminal, we should at least be able to show some colors.&lt;/p&gt;
&lt;p&gt;Here is what it looks like if you try to render True Colors in a terminal emulator that doesn&#39;t support them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/terminal-colors-no-true-color.png&quot; alt=&quot;The lime green background isn&#39;t visible, because this terminal emulator doesn&#39;t support true colors.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And here is the same colors converted down to the more limited ANSI 256 color space.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/terminal-colors-true-color-to-ansi256.png&quot; alt=&quot;Converting colors down to the more limited ANSI 256 may not look the same, but it captures the original visual intent.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Sure, the colors are slightly different, but it&#39;s better than nothing. It&#39;s a &amp;quot;good enough&amp;quot; compromise that keeps the original intentions intact. As far as I know this is only needed for macOS&#39;s &lt;code&gt;Terminal.app&lt;/code&gt;, which unfortunately is the one I happen to use the most. For some reason I haven&#39;t switched to another emulator so far.&lt;/p&gt;
&lt;h2&gt;Can we do better?&lt;/h2&gt;
&lt;p&gt;Despite all those advancements, it feels a little weird to have to be aware of ANSI escape codes as a developer. Most developers just want to set the text color or background color after all. You know browsers allow you to style &lt;code&gt;console.log&lt;/code&gt; messages via plain CSS. What if we leveraged the same thing on the server? That&#39;s exactly what &lt;a href=&quot;https://deno.com/runtime&quot;&gt;deno&lt;/a&gt; does. They got it right. They allow you to use the same API to print colors on the server.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;%cThis text is lime green %cand %cthis text has a red background&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;color: #86efac&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;background-color: red; color: white&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rendered in Chrome&#39;s browser console:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/terminal-colors-chrome.png&quot; alt=&quot;Screenshot of the console in Chrome rendering the colored output&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The same code executed on the server with &lt;a href=&quot;https://deno.com/runtime&quot;&gt;deno&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/terminal-colors-deno.png&quot; alt=&quot;Screenshot of iterm showing the colored output by deno&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Sometimes it&#39;s all about getting the details right. For me colors have always been a huge part in making CLI output more readable. They can introduce an additional visual hierarchy that&#39;s not possible with mere character shapes. Fixing color detection support for some projects was a fun little investigation. Luckily, most of the complexity is already solved by existing libraries in the ecosystem, so that you don&#39;t have to work this out yourself.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Speeding up the JavaScript ecosystem - draft-js emoji plugin</title>
    <link href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-5/"/>
    <updated>2023-04-16T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-5/</id>
    <content type="html">&lt;p&gt;I received a very interesting issue via email from &lt;a href=&quot;http://www.joshuakgoldberg.com/&quot;&gt;Josh Goldberg&lt;/a&gt; regarding a website that froze for about 2-3s. The website uses the &lt;a href=&quot;https://draftjs.org/&quot;&gt;draft-js&lt;/a&gt; rich text editor for some inputs and he was able to narrow it down to something going wrong in the emoji plugin for draft-js. So we decided to hop on a call and continue debugging together.&lt;/p&gt;
&lt;p&gt;Capturing a quick recording via Chrome&#39;s profiler confirms the initial suspicions. Something is up with the emoji plugin. We can see a lot of frequent function calls at the bottom that take up the majority of time. Unfortunately, Chrome&#39;s profiler doesn&#39;t have some sort of &amp;quot;left-heavy&amp;quot; visualization like &lt;a href=&quot;https://www.speedscope.app/&quot;&gt;speedscope&lt;/a&gt; does. This makes it a little harder to see which function is worth investigating. The &amp;quot;left-heavy&amp;quot; is nicer for that as it merges similar callstacks into one.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-emoji-profile.png&quot; alt=&quot;Chrome profile showing a gazillion RegExp calls.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Each individual call seemed fine and always ended up with calling into the regex engine. But the high number of calls were enough of a reason for concern. A cool thing about Chrome&#39;s profiler is that it can annotate source code lines with the sampled traces. This gives you an approximate time for how much each line took to execute. It&#39;s not 100% accurate due to the amount of transpilation being involved in frontend projects, but it&#39;s good enough to derive some early conclusions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-emoji-line-sample.png&quot; alt=&quot;Chrome Source panel showing 2.2s being spent inside escapeRegExp and 550ms inside replaceAll&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Aha! The two methods that consumed most of the time deal with regexes. It might have been tempting to conclude that the regexes itself were to blame, but I had a feeling that that was merely a symptom of a deeper problem. First thing we checked was how often this function was called. This can be done via incrementing a simple counter or by using &lt;code&gt;console.count()&lt;/code&gt; directly.&lt;/p&gt;
&lt;pre class=&quot;language-diff-js&quot;&gt;&lt;code class=&quot;language-diff-js&quot;&gt;&lt;span class=&quot;token unchanged language-js&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; ns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;escapeRegExp&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-js&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;   console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;escapeRegExp&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged language-js&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;[-[&#92;]{}()*+?.,;:&amp;amp;&#92;&#92;^$#&#92;s]&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&#92;&#92;$&amp;amp;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token unchanged language-js&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; ns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;replaceAll&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;string&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-js&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;   console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;replaceAll&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged language-js&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; escapedFind &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;escapeRegExp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;find&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; search &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RegExp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;object[^&gt;]*&gt;.*?&amp;lt;&#92;/object&gt;|&amp;lt;span[^&gt;]*&gt;.*?&amp;lt;&#92;/span&gt;|&amp;lt;(?:object|embed|svg|img|div|span|p|a)[^&gt;]*&gt;|(&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;escapedFind&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;gi&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Turns out that the &lt;code&gt;replaceAll&lt;/code&gt; method was called &lt;code&gt;7318&lt;/code&gt; times whenever the page was loaded. Next, we checked what kind of arguments &lt;code&gt;escapeRegExp&lt;/code&gt; was called with. The theory being that maybe it&#39;s called with the same arguments over and over again.&lt;/p&gt;
&lt;p&gt;A minute later that hypothesis proved to be correct as it was escaping the same string over and over again. We know that this method is called from &lt;code&gt;replaceAll&lt;/code&gt;, so let&#39;s check if we&#39;re always passing the same argument there. And sure enough, the first &lt;code&gt;string&lt;/code&gt; argument received two different values, but the second &lt;code&gt;find&lt;/code&gt; argument was always the same. That&#39;s the one that was later passed to &lt;code&gt;escapeRegExp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The immediate question that popped up was: &amp;quot;Who is calling &lt;code&gt;replaceAll&lt;/code&gt; and why are they always passing the same arguments?&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-emoji-short-profile.png&quot; alt=&quot;Zoomed in screenshot of Chrome Profiler showing that all calls to replaceAll originate from toShort&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Zooming into the profile again, we observed that all calls to &lt;code&gt;replaceAll&lt;/code&gt; had &lt;code&gt;toShort&lt;/code&gt; as a common ancestor.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-emoji-short.png&quot; alt=&quot;The source of toShort creates a variable inline that is then passed as the find parameter to replaceAll&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Something very interesting was happening here in that the second argument passed to &lt;code&gt;replaceAll&lt;/code&gt; didn&#39;t depend on arguments passed to &lt;code&gt;toShort&lt;/code&gt; at all. And following the trail of &lt;code&gt;unicodeCharRegex&lt;/code&gt; we got a clearer picture of what the purpose of the code here was. Like in popular chat apps like Slack, the plugin for draft-js allows you to type the text &lt;code&gt;:smile:&lt;/code&gt; which is then automatically converted to a proper emoji &amp;quot;😀&amp;quot;. But the reverse is also needed and that&#39;s what we were seeing here.&lt;/p&gt;
&lt;h2&gt;Finding a solution&lt;/h2&gt;
&lt;p&gt;Knowing more about the purpose of this code, we noticed that the the regex was built by iterating a big data structure of unicode characters and their metadata. This regex was then applied to the incoming text to match emojis and replace them with the shortcode. With every single call to &lt;code&gt;toShort&lt;/code&gt; the regex would be constructed from scratch. It becomes a performance problem, because the unicode standard has a gazillion emojis with variations for skin tones and other things. So yeah, no wonder the resulting regex was huge.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-emoji-regex.png&quot; alt=&quot;The joined string of emojis before passing it to the regex constructor&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Chrome&#39;s console is showing that the generated string from which the regex is constructed is bigger than &lt;code&gt;42kB&lt;/code&gt; alone.&lt;/p&gt;
&lt;p&gt;As with most cases of expensive computations, we can avoid a lot of the work by caching the previous result. That way we don&#39;t need to recompute the same thing again and again. It&#39;s the least invasive fix one can make that doesn&#39;t require bigger changes to the architecture of the plugin. We &lt;a href=&quot;https://github.com/joypixels/emoji-toolkit/pull/57&quot;&gt;made a PR&lt;/a&gt; and it reduced the blocking time from &lt;code&gt;2-3s&lt;/code&gt; down to &lt;code&gt;&amp;lt;200ms&lt;/code&gt; in total. That&#39;s still a lot more than what we&#39;d like, but that&#39;s already night and day in terms of user experience.&lt;/p&gt;
&lt;p&gt;We ended our call together at this stage, but it made me wonder what else could be done if we didn&#39;t restrict ourselves to keep the current architecture.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If we step back for a second, it seems wasteful to always build up that regex whenever that module is loaded in the first place. The result is always the same, so an optimization would be to store the transformed result and use that from the get go in the plugin instead. That could be built whenever a new version of the plugin is released.&lt;/p&gt;
&lt;p&gt;Another potential idea to pursue would be to use a handcrafted function to match emojis. It would allow you to narrow down the search space of potential matches very quickly, but it remains to verify that such an approach would be faster than a regex in this scenario. I think it&#39;s worth a shot.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;EDIT: Fabio Spampinato shared an even better idea on &lt;a href=&quot;https://twitter.com/fabiospampinato/status/1647606467438968832&quot;&gt;twitter&lt;/a&gt;. Instead of constructing a &amp;gt;40kB regular expressions, we can leverage the recent unicode enhancements. This includes special unicode property escapes like &lt;code&gt;Emoji_Presentation&lt;/code&gt; which allows you to match all emojis directly (example: &lt;code&gt;/&#92;p{Emoji_Presentation}/gu&lt;/code&gt;). With that we can get rid of all the regex generation code completly. Read more about that on &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions/Unicode_property_escapes&quot;&gt;MDN&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Overall this particular issue is a good reminder to profile our code from time to time. It&#39;s a reminder that even innocent looking functions can have a huge impact on performance.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Speeding up the JavaScript ecosystem - npm scripts</title>
    <link href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-4/"/>
    <updated>2023-03-19T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-4/</id>
    <content type="html">&lt;p&gt;If you’re working with JavaScript you’ve likely used the &lt;code&gt;&amp;quot;scripts&amp;quot;&lt;/code&gt; field in &lt;code&gt;package.json&lt;/code&gt; to set up common tasks for your project. Those scripts can be executed with &lt;code&gt;npm run&lt;/code&gt; on the terminal. I noticed that I opted more and more to just call the underlying command directly instead of shelling out to &lt;code&gt;npm run&lt;/code&gt;, mostly because it&#39;s noticeably faster. But what makes them so much slower in comparison? Time for a profiling session!&lt;/p&gt;
&lt;h2&gt;Loading only the code you need&lt;/h2&gt;
&lt;p&gt;What many developers don’t know is that the npm CLI is a standard JavaScript file and can be invoked like any other &lt;code&gt;.js&lt;/code&gt; file. On macOS and Linux you can get the full path to the npm cli by running &lt;code&gt;which npm&lt;/code&gt;. Dumping that file to the terminal reveals that it&#39;s a boring standard &lt;code&gt;.js&lt;/code&gt; file. The only special thing is the first line which tells your shell with which program the current file can be executed with. Since we&#39;re dealing with a JavaScript file that would be &lt;code&gt;node&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-npm-prof.png&quot; alt=&quot;Inspecting the npm binary by calling it with node&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Because it&#39;s just a &lt;code&gt;.js&lt;/code&gt; file we can rely on all the usual ways to generate a profile. My favorite one is node’s &lt;code&gt;--cpu-prof&lt;/code&gt; argument. Combine that knowledge together and we can generate a profile from an npm script via &lt;code&gt;node --cpu-prof $(which npm) run myscript&lt;/code&gt;. Loading that profile into speedscope reveals quite a bit about how npm is structured.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-npm-expensive.png&quot; alt=&quot;Profile of the npm run command shows that nearly all time is spent on loading the modules that npm cli is composed of.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The majority of time is spent on loading all the modules that compose the npm cli. The time of the script that we’re running pales in comparison. We see a bunch of files which only seem to be necessary once certain conditions are met. An example of that would be code for formatting error messages that&#39;s only needed when an error occurred.&lt;/p&gt;
&lt;p&gt;Such a case exists in npm where an exit handler is required eagerly. Let&#39;s require that module only when we need it.&lt;/p&gt;
&lt;pre class=&quot;language-diff-javascript&quot;&gt;&lt;code class=&quot;language-diff-javascript&quot;&gt;&lt;span class=&quot;token unchanged language-javascript&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// in exit-handler.js&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; log &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./log-shim.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted language-javascript&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; errorMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./error-message.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; replaceInfo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./replace-info.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token unchanged language-javascript&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;exitHandler&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-javascript&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;     &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; replaceInfo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./replace-info.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;     &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; errorMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./error-message.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged language-javascript&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Comparing the profile after this change with the one without it doesn&#39;t show any difference in the total time taken. That is because the modules that we changed to be lazily loaded here are eagerly required elsewhere. To properly lazy load them we need to change all sites where it&#39;s required.&lt;/p&gt;
&lt;p&gt;Next up I noticed that a bunch of code related to npm’s audit feature was loaded. This seems odd, since I’m not running anything related to auditing. Unfortunately for us, it’s not as easy as just moving some &lt;code&gt;require&lt;/code&gt; calls around.&lt;/p&gt;
&lt;h2&gt;One class to rule them all&lt;/h2&gt;
&lt;p&gt;A recurring problem in various js tools is that they are composed of a couple of big classes that pull in everything instead of only the code you need. Those classes always start out small and with good intentions to be lean, but somehow they just get bigger and bigger. It becomes harder and harder to ensure that you&#39;re only loading the code you need. This reminds me of this quote from Joe Armstrong (creator of the Erlang programming language):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.&amp;quot; – Joe Armstrong&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Inside npm is an &lt;code&gt;Arborist&lt;/code&gt; class which pulls in a lot of things that are only needed for specific commands. It pulls in things related to modifying the layout and packages in &lt;code&gt;node_modules&lt;/code&gt;, auditing package versions and a bunch of other things that are not needed for the &lt;code&gt;npm run&lt;/code&gt; command. If we want to optimize &lt;code&gt;npm run&lt;/code&gt; we need to boot them off of the eagerly loaded modules list.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mixins &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;../tracker.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./pruner.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./deduper.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./audit.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./build-ideal-tree.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./load-workspaces.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./load-actual.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./load-virtual.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./rebuild.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./reify.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./isolated-reifier.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Base &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mixins&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;events&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arborist&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Base&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For our purposes all the modules loaded in the mixins array, which the &lt;code&gt;Arborist&lt;/code&gt; class later extends on, are not needed. We can drop all of them. This change gains us savings of about 20ms, which might not seem like much, but those savings add up. Like before, we need to check other places where those modules are required to ensure that we&#39;re really loading it on demand only.&lt;/p&gt;
&lt;h2&gt;Reducing the size of the module graph&lt;/h2&gt;
&lt;p&gt;Changes to a couple of require statements here and there are nice, but won&#39;t sway the numbers substantially. The bigger issue are dependencies which often have one main entry file which pulls in all the code of said module. Ultimately, the problem is that when the engine sees a bunch of top level &lt;code&gt;import&lt;/code&gt; or &lt;code&gt;require&lt;/code&gt; statements, it is going to parse and load those modules eagerly. No exceptions. But this is exactly what we want to avoid here.&lt;/p&gt;
&lt;p&gt;A concrete example of that is the &lt;code&gt;cleanUrl&lt;/code&gt; function that is imported from the &lt;code&gt;npm-registry-fetch&lt;/code&gt; package. Like the name implies, that package is all about doing network stuff. But we don&#39;t do any sort of network requests in &lt;code&gt;npm run&lt;/code&gt; when running a script. That&#39;s another 20ms saved. We don&#39;t need to display a progress bar either, so we can drop code for that too. Same is true for a bunch of other dependencies that the npm cli uses.&lt;/p&gt;
&lt;p&gt;The amount of modules you load is a very real problem for these scenarios. It&#39;s no surprise that libraries for which start up time is critical have turned to bundlers to merge all their code into fewer files. Engines are pretty good with loading big blobs of JavaScript. The main reason we care so much about file size on the web is the cost of having to deliver those bytes over the network.&lt;/p&gt;
&lt;p&gt;There are tradeoffs to this approach though. The bigger the file, the longer it takes to parse it, so there will be a threshold after which the parse cost of a single gigantic file is higher than splitting it up. As always: Measuring will show you if you hit that tradeoff. Another thing to consider is that bundlers cannot bundle code written for the CommonJS module system as efficiently as code that’s authored in ESM. Typically they introduce a lot of wrapper code around CommonJS modules which nullifies most of the benefits of bundling the code in the first place.&lt;/p&gt;
&lt;h2&gt;Sorting all the strings&lt;/h2&gt;
&lt;p&gt;With each reduction of the module graph the profile became less noisy and revealed other areas which could be improved. A particular call to a &lt;code&gt;collaterCompare&lt;/code&gt; function caught my eye.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-npm-collator.png&quot; alt=&quot;The collator compare function takes up 10ms in the profile&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You might be thinking that 10ms isn’t worth it to spend time investigating it, but in this profile it&#39;s more of a &amp;quot;death by a thousand paper cuts&amp;quot; sort of thing. There is no single big entry that makes everything fast. So spending improving even smaller call sites is very much worth it. What’s interesting about this one for the &lt;code&gt;collatorCompare&lt;/code&gt; function is that it’s intended purpose is to sort strings in a locale aware fashion. The implementation is split in two parts to achieve that: An initializing function and the function it returns which does the actual comparison.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Simplified example of the code in @isaacs/string-locale-compare&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;collatorCompare&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;locale&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; collator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Collator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;locale&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Always returns a new function that needs to be optimized from scratch&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; collator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;locale&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;locale&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#92;n&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; compare &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;collatorCompare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;locale&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; compare&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; compare&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we look at all the places this module is loaded at, we can see that we are only ever interested in sorting English strings and never pass any additional options aside from the locale. But because of the way this module is structured, every new &lt;code&gt;require&lt;/code&gt; call will nudge folks to create a brand new comparison function that needs to be optimized all over again.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Every require call immediately calls the &quot;default&quot; export with &quot;en&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; localeCompare &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;@isaacs/string-locale-compare&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But ideally we want everyone to use the same comparison function. With that in mind we can replace the code with a two-liner where we create the &lt;code&gt;Intl.Collator&lt;/code&gt; once and create the &lt;code&gt;localeCompare&lt;/code&gt; function only once too.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// We only ever need to construct the Collator class instance once&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; collator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Collator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;localeCompare&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; collator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At one particular place, npm holds a sorted list of available commands. That list is hard coded and will never change at runtime. It only consists of ascii strings, so we can use plain old &lt;code&gt;.sort()&lt;/code&gt; instead of our locale aware function.&lt;/p&gt;
&lt;pre class=&quot;language-diff-javascript&quot;&gt;&lt;code class=&quot;language-diff-javascript&quot;&gt;&lt;span class=&quot;token unchanged language-javascript&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// This array only contains ascii strings&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; commands &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token string&quot;&gt;&#39;access&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token string&quot;&gt;&#39;adduser&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token string&quot;&gt;&#39;audit&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token string&quot;&gt;&#39;bugs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token string&quot;&gt;&#39;cache&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token string&quot;&gt;&#39;ci&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted language-javascript&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;localeCompare&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-javascript&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this change the time it takes to call that function is close to 0ms. Another 10ms saved since this was the last place which loaded this module eagerly.&lt;/p&gt;
&lt;p&gt;To note here is that at this point we’ve made &lt;code&gt;npm run&lt;/code&gt; twice as fast. We’re now down to ~200ms from the ~400ms we started with. Looking good!&lt;/p&gt;
&lt;h2&gt;Setting process.title is expensive&lt;/h2&gt;
&lt;p&gt;Another function call that jumped out was this call to a setter for a mysterious &lt;code&gt;title&lt;/code&gt; property. 20ms for setting a property seems expensive.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-npm-process-title.png&quot; alt=&quot;The profile shows that setting process.title takes 19.71ms&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The implementation of that setter is surprisingly simple:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Npm&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EventEmitter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// This line is the culprit&lt;/span&gt;&lt;br /&gt;		process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;#title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Changing the title of the current running process seems to be a pretty expensive operation. This feature is really useful though, because it makes it easier to spot specific npm processes in the task manager when you have multiple ones running at the same time. For the sake of this investigation I didn’t look further and commented that part out. Nonetheless, I think it might be worth to look deeper into what&#39;s making it so costly.&lt;/p&gt;
&lt;h2&gt;Globbing log files&lt;/h2&gt;
&lt;p&gt;Another entry in the profile that drew my attention was a call to another string sorting function inside the &lt;code&gt;glob&lt;/code&gt; module. It’s pretty odd that we’re even globbing here at all when all we want to do is run npm scripts. The &lt;code&gt;glob&lt;/code&gt; module is used to crawl the file system for files matching a user defined pattern, but why would we need that? The majority of time seems to be spent not searching the file system but rather sorting strings ironically.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-npm-glob.png&quot; alt=&quot;The alphasort function of the glob module consumes 10.4ms in the profile.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This function is only called once with a simple array of 11 strings and sorting that should be instant. Strangely, the profile showed that this took ~10ms.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Sorting this array somehow takes 10ms&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;/Users/marvinhagemeister/.npm/_logs/2023-03-18T20_06_53_324Z-debug-0.log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;/Users/marvinhagemeister/.npm/_logs/2023-03-18T20_07_35_219Z-debug-0.log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;/Users/marvinhagemeister/.npm/_logs/2023-03-18T20_07_36_674Z-debug-0.log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;/Users/marvinhagemeister/.npm/_logs/2023-03-18T20_08_11_985Z-debug-0.log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;/Users/marvinhagemeister/.npm/_logs/2023-03-18T20_09_23_766Z-debug-0.log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;/Users/marvinhagemeister/.npm/_logs/2023-03-18T20_11_30_959Z-debug-0.log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;/Users/marvinhagemeister/.npm/_logs/2023-03-18T20_11_42_726Z-debug-0.log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;/Users/marvinhagemeister/.npm/_logs/2023-03-18T20_12_53_575Z-debug-0.log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;/Users/marvinhagemeister/.npm/_logs/2023-03-18T20_17_08_421Z-debug-0.log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;/Users/marvinhagemeister/.npm/_logs/2023-03-18T20_21_52_813Z-debug-0.log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;/Users/marvinhagemeister/.npm/_logs/2023-03-18T20_24_02_611Z-debug-0.log&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The implementation looks pretty harmless too.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;alphasort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;localeCompare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But maybe we can use the &lt;code&gt;Intl.Collator&lt;/code&gt; object instead that we used earlier to compare these strings.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; collator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Intl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Collator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;en&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;alphasort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; collator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;compare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that did the trick. I’m not entirely sure why &lt;code&gt;String.prototype.localeCompare&lt;/code&gt; is slower in comparison. It definitely sounds fishy. But I can reliably verify the speed difference on my end. The &lt;code&gt;Intl.Collator&lt;/code&gt; approach is consistently faster for this particular call.&lt;/p&gt;
&lt;p&gt;The bigger fish is that searching the file system for log files seems at odds with our intentions. It’s super useful that a log file is written and cleared if the command was successful, but shouldn’t we know the names of the files we wrote if we are the ones who created them in the first place? I took a stab at changing that, but for the sake of this article I decided to comment that out and continue the investigation.&lt;/p&gt;
&lt;p&gt;At this point we’re down to ~138ms from the ~400ms at the beginning. Although that’s already a pretty nice improvement we could do a lot better.&lt;/p&gt;
&lt;h2&gt;Deleting all the things&lt;/h2&gt;
&lt;p&gt;I felt like I needed to become a little more aggressive with deleting or uncommenting code that’s not related to running npm scripts. We’ve done our fair share so far and we could continue on that path, but I got curious about what the northstar time is that we should shoot for. The basic goal is to only load code that is absolutely necessary to execute npm scripts. Everything else is merely overhead and time wasted.&lt;/p&gt;
&lt;p&gt;So I went out and wrote a short script that does the bare minimum necessary to run npm scripts. In the end I got it down to about 22ms, which is about 18x faster than the 400ms we started with. I&#39;m pretty happy with that although 22ms still feels like a lot of time compared to what it does. It&#39;s certainly something where other languages like Rust excel at in comparison. Regardless, there is a point to be made that 22ms is fast enough for now.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-npm-custom.png&quot; alt=&quot;The profile of the custom runner script only needs ~22ms in total&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;On the surface it seems strange that we spent that much time making the &lt;code&gt;npm run&lt;/code&gt; command about 380ms faster. However, if you think about how often that command is executed by developers all over the world and how often it is executed inside a CI, those savings add up pretty quickly. It&#39;s nice to have have more snappier npm scripts too for local development too, so there is definitely the angle of personal benefit&lt;/p&gt;
&lt;p&gt;But the big elephant in the room remains: There is no easy way to short circuit the module graph. All JavaScript tools I looked at so far have this problem. Some tools have this more pronounced and others are less affected. The overhead of resolving and loading a bunch of modules is very real. I’m not sure what the long term solution to that will be or if that is solvable by JavaScript engines themselves.&lt;/p&gt;
&lt;p&gt;Until a proper solution is found, a viable workaround that we can apply today is bundling the code when publishing it to npm. I’m secretly hoping though that this isn’t the only viable path forward and that all runtimes improve on that front. The less tooling we have to deal with, the more beginner friendly we are as an ecosystem.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Speeding up the JavaScript ecosystem - eslint</title>
    <link href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-3/"/>
    <updated>2023-02-05T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-3/</id>
    <content type="html">&lt;p&gt;We&#39;ve talked quite a bit about linting in the &lt;a href=&quot;https://marvinh.dev/blog/speeding-up-javascript-ecosystem/&quot;&gt;past&lt;/a&gt; &lt;a href=&quot;https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-2/&quot;&gt;two&lt;/a&gt; posts of this series, so I thought it&#39;s time to give &lt;a href=&quot;https://github.com/eslint/eslint&quot;&gt;eslint&lt;/a&gt; the proper limelight it deserves. Overall eslint is so flexible, that you can even swap out the parser for a completely different one. That&#39;s not a rare scenario either as with the rise of JSX and TypeScript that is frequently done. Enriched by a healthy ecosystem of plugins and presets, there is probably a rule for every use case out there and if there isn&#39;t, the excellent documentation guides you on how to create your own rules. That&#39;s one thing I want to highlight here as it&#39;s a project that has stood the test of time.&lt;/p&gt;
&lt;p&gt;But this also poses a problem for performance profiling as due to the vastness of the configuration flexibility two projects can have a widely different experience when it comes to linting performance. We need to start somewhere though, so I figured what better way to start our investigation than to look at the linting setup used in the eslint repository itself!&lt;/p&gt;
&lt;h2&gt;Using eslint to lint eslint&lt;/h2&gt;
&lt;p&gt;Their repository uses a task runner abstraction to coordinate common build tasks, but with a little bit of digging we can piece together the command that is run for the &amp;quot;lint&amp;quot; task, specifically for linting JavaScript files.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; bin/eslint.js --report-unused-disable-directives &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; --ignore-pattern &lt;span class=&quot;token string&quot;&gt;&quot;docs/**&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Neat, here you have it: Eslint is using eslint to lint their codebase! Like in both previous articles of this series we&#39;ll be generating a &lt;code&gt;*.cpuprofile&lt;/code&gt; via node&#39;s built-in &lt;code&gt;--cpu-prof&lt;/code&gt; argument and we&#39;ll be loading that into &lt;a href=&quot;https://www.speedscope.app/&quot;&gt;Speedscope&lt;/a&gt; for further analysis. A few seconds later (22s to be exact), we are ready to dive in!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-eslint-lint-eslint.png&quot; alt=&quot;Screenshot of speedscope of a profile where we used eslint to lint eslint. We can see that jsdoc linting and selector matching takes up a huge chunk of time&quot; /&gt;&lt;/p&gt;
&lt;p&gt;By merging similar call-stacks together we get a much clearer picture of where the time is being spent. This is typically referred to as a &amp;quot;left-heavy&amp;quot; visualization. This is not to be confused with your standard flamegraph where the x-axis represents when a call happened. Instead, in this style the x-axis represents time consumed of the total time, not when it happened. To me it&#39;s one of the main benefits of &lt;a href=&quot;https://www.speedscope.app/&quot;&gt;Speedscope&lt;/a&gt; and it feels much snappier too. Wouldn&#39;t have expected any less since it&#39;s written by a few devs from Figma who are known for their excellence in engineering in our industry.&lt;/p&gt;
&lt;p&gt;Immediately we can make out a few key areas where the linting setup in eslint&#39;s repository is spending time. The main things that stand out is that a good chunk of the total time is spent on rules dealing with JSDoc as inferred from their function names. Another interesting aspect is that there are two different parsers running at various times during the lint task: esquery and acorn. But my curiosity was piqued by the JSDoc rules taking so long.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-jsdoc-token.png&quot; alt=&quot;Screenshot of speedscope showing BackwardTokenCommentCursor takes 1.3s&quot; /&gt;&lt;/p&gt;
&lt;p&gt;One particular &lt;code&gt;BackwardTokenCommentCursor&lt;/code&gt; entry seems to be of interest as it&#39;s the largest block of the bunch. Following the attached file location to the source, it seems to be a class that holds the state of where we are in a file. As a first measure I added a plain counter that increments whenever that class is instantiated and ran the lint task again.&lt;/p&gt;
&lt;h2&gt;On getting lost 20 million times&lt;/h2&gt;
&lt;p&gt;All in all, this class has been constructed more than 20 million times. That seems quite a lot. Remember, any object or class we instantiate takes up memory, and that memory later needs to be cleaned up. We can see the consequence of that in the data where garbage collection (the act of cleaning up memory) takes 2.43s in total. That&#39;s not good.&lt;/p&gt;
&lt;p&gt;Upon creating a new instance of that class it calls two functions which both seem to kick off a search. Without knowing more about what it&#39;s doing the first one can be ruled out though as it doesn&#39;t contain any form of loops. From experience, loops are usually a prime suspect to investigate for performance, so I usually start my search there.&lt;/p&gt;
&lt;p&gt;The second function though, called &lt;code&gt;utils.search()&lt;/code&gt;, contains a loop. It loops over the stream of tokens that were parsed from the contents of the file we were linting at the time. A token is the smallest building block of a programming language and you can think of them being the &amp;quot;words&amp;quot; of a language. For example in JavaScript the word &amp;quot;function&amp;quot; is typically represented as one function token, and so is a comma or a single semicolon. In this &lt;code&gt;utils.search()&lt;/code&gt; function we seem to be concerned with finding the nearest token to our current position in the file.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;search&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;tokens&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; index &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;el&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; location &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getStartLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;el&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; index &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; tokens&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To do that the search is done on the token array via JavaScript&#39;s native &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex&quot;&gt;&lt;code&gt;.findIndex()&lt;/code&gt;&lt;/a&gt; method. The algorithm is described as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;findIndex()&lt;/code&gt; is an iterative method. It calls a provided &lt;code&gt;callbackFn&lt;/code&gt; function once for each element in an array in ascending-index order, until &lt;code&gt;callbackFn&lt;/code&gt; returns a truthy value.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Given that the array of tokens grows with the amount of code we have in a file, that doesn&#39;t sound ideal. There are more efficient algorithms to search a value in an array that we can use rather than going through every element in the array. Replacing that line with a binary search for example cuts the time in half.&lt;/p&gt;
&lt;p&gt;Whilst a 50% reduction seems good, it still doesn&#39;t address that this code is called 20 million times. To me that&#39;s the real issue. We&#39;re more or less trying to reduce the impact of the symptoms here instead of addressing the underlying problem. We&#39;re already iterating through the file so &lt;strong&gt;we should know exactly where we are&lt;/strong&gt;. Changing that requires a more invasive refactor though and would be too much to explain for this blog post. Seeing that this wouldn&#39;t be an easy fix I checked what else is worthy of attention in the profile. The long violet bars in the center are hard to miss, not just because they&#39;re a different color, but because they take up a lot of time and are not drilled down into hundreds of smaller function calls.&lt;/p&gt;
&lt;h2&gt;The selector engine&lt;/h2&gt;
&lt;p&gt;The callstack in speedscope points to a project called &lt;a href=&quot;https://github.com/estools/esquery&quot;&gt;esquery&lt;/a&gt; which I hadn&#39;t heard about prior to this investigation. It&#39;s an older project whose goal is to be able to find a specific object in the parsed code through a small selector language. If you squint a little you can see the strong resemblance to CSS selectors. And they work the same way here, just that we&#39;re not finding a specific HTML element in the DOM tree, but rather an object in another tree structure. It&#39;s the same idea.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-eslint-mangled.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The traces indicate that the npm package ships with minified source code. Mangled variable names which are typically a single character strongly hint at such a process being in place. Luckily for me, the package also ships an unminified variant as well so I just modified the &lt;code&gt;package.json&lt;/code&gt; to point at that instead. Another run later and we&#39;re greeted with the following data:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-eslint-get-path.png&quot; alt=&quot;Screenshot of speedscope showing that the selector engine consumes a couple of seconds alone&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Much better! A thing to keep in mind with unminified code is that it performs about 10-20% slower than the minified variant. That&#39;s a rough approximate range which I&#39;ve measured many times throughout my career when comparing the performance of minified to unminified code. Nonetheless, the relative timings stay the same, so it remains perfect for our investigation. With that the &lt;code&gt;getPath&lt;/code&gt; function seems like something that could use a little help.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; keys &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; _iterator &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_createForOfIteratorHelper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keys&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		_step&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_iterator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_step &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; _iterator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;done&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; _key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; _step&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;			obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;_key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		_iterator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		_iterator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Outdated transpilation will haunt us for a long time&lt;/h2&gt;
&lt;p&gt;If you&#39;ve been around the JavaScript tooling space for a while these functions look awfully familiar. &lt;code&gt;_createForOfIteratorHelper&lt;/code&gt; is with 99.99% certainty a function that was inserted by their publishing pipeline and not by the authors of this library. When &lt;code&gt;for-of&lt;/code&gt; loops were added to JavaScript it took a while until it was supported everywhere.&lt;/p&gt;
&lt;p&gt;Tools that downtranspile modern JavaScript features tend to err on the side of caution and rewrite the code in a very conservative way. In this case we know that we&#39;re splitting a &lt;code&gt;string&lt;/code&gt; into an array of strings. To loop over that using a full blown iterator is totally overkill and a boring standard for loop would&#39;ve been all that was needed. But since tools are not aware of that, they went with the variant that covers the most scenarios possible. Here is the original code for comparison:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; keys &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; key &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; keys&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In today&#39;s world &lt;code&gt;for-of&lt;/code&gt; loops are supported everywhere, so I patched the package again and swapped out the function implementation with the original one in the source. This one single change saves about 400ms. I&#39;m always impressed by how much CPU time we burn on wasteful polyfills or outdated downtranspilation. You&#39;d think that it makes much less of a difference, but then you came across a case such as this where the numbers paint a different picture. For what it&#39;s worth I also measured replacing the &lt;code&gt;for-of&lt;/code&gt; loop with a standard for loop.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; keys &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; keys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; keys&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Surprisingly, this netted another 200ms improvement compared to the &lt;code&gt;for-of&lt;/code&gt; variant. I guess even today, &lt;code&gt;for-of&lt;/code&gt; loops are harder to optimize for engines. That reminds me of a &lt;a href=&quot;https://github.com/graphql/graphql-js/pull/3687&quot;&gt;past investigation&lt;/a&gt; &lt;a href=&quot;https://www.jovidecroock.com/&quot;&gt;Jovi&lt;/a&gt; and I did into a sudden slow down in the parsing speed of the &lt;code&gt;graphql&lt;/code&gt; package when they made a new release in which they switched to &lt;code&gt;for-of&lt;/code&gt; loops.&lt;/p&gt;
&lt;p&gt;It&#39;s something a v8/gecko/webkit engineer can properly verify, but my assumption would be that it still has to invoke the iterator protocol as that could have been globally overwritten, which would change the behavior for every single Array. It&#39;s probably something like that.&lt;/p&gt;
&lt;p&gt;Whilst we got some quick wins out of those changes, it&#39;s still far from ideal. Overall the function remains a top contender to be improved as it is alone responsible for a couple of seconds of the total time. Applying the quick counter hack again reveals that it&#39;s called about 22k times. For sure a function that&#39;s somewhat in the &amp;quot;hot&amp;quot; path.&lt;/p&gt;
&lt;p&gt;Of particular note in many performance intensive code that deals with strings revolves around the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split&quot;&gt;&lt;code&gt;String.prototype.split()&lt;/code&gt;&lt;/a&gt; method. This will effectively iterate over all characters, allocate a new array and then iterate over that, all of which could be done in a single iteration.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; last &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Fine because all keys are ASCII and not unicode&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;last&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			last &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;last&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This rewrite had a strong impact on its performance. When we started &lt;code&gt;getPath&lt;/code&gt; took &lt;strong&gt;2.7s&lt;/strong&gt; in total and with all optimizations applied we managed to get that down to &lt;strong&gt;486ms&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-eslint-get-path-2.png&quot; alt=&quot;Screenshot of speedscope showing that the getPath function barely consumes any time after our optimizations&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Continuing with the &lt;code&gt;matches()&lt;/code&gt; function, we see a lot of overhead created by weird &lt;code&gt;for-of&lt;/code&gt; downtranspilation, similar to what we&#39;ve seen earlier. To save some time I just went on github and copied the function from the source code directly. Since &lt;code&gt;matches()&lt;/code&gt; is even more prominent in the traces, this change alone saves a full &lt;strong&gt;1s&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So many libraries in our ecosystem suffer from this issue. I really wish there was a way to update them all with one click. Maybe we need a reverse transpiler which detects down transpilation patterns and converts it back to modern code again.&lt;/p&gt;
&lt;p&gt;I reached out to &lt;a href=&quot;https://infosec.exchange/@jviide&quot;&gt;jviide&lt;/a&gt; to see if we could optimize &lt;code&gt;matches()&lt;/code&gt; further. &lt;a href=&quot;https://github.com/estools/esquery/compare/master...jviide:esquery:master&quot;&gt;With his additional changes&lt;/a&gt; we were able to make the whole selector code about 5x faster compared to the original unmodified state. What he basically did is get rid of a bunch of overhead in the &lt;code&gt;matches()&lt;/code&gt; function which allowed him to simplify a couple of related helper functions too. For example he noticed that template strings were down transpiled poorly.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// input&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; literal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;selector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// output: down transpiled, slow&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; literal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;He went even a step further by parsing each new selector into a chain of function calls on the fly and caching the resulting wrapper function. This trick netted him another hefty speedup for the selector engine. I highly recommend checking out &lt;a href=&quot;https://github.com/estools/esquery/compare/master...jviide:esquery:master&quot;&gt;his changes&lt;/a&gt;. We haven&#39;t opened a PR because &lt;code&gt;esquery&lt;/code&gt; seems to be unmaintained at this point.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;EDIT: We did open &lt;a href=&quot;https://github.com/estools/esquery/pull/134&quot;&gt;a PR in the end&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Bailing out early&lt;/h2&gt;
&lt;p&gt;Sometimes it&#39;s good to take a step back and tackle the problem from a different angle. So far we looked at implementation details, but what kind of selectors are we actually dealing with? Is there potential to short circuit some of them? To test that theory I first needed to get a better overview of the kind of selectors that are being processed. Unsurprisingly, the majority of selectors were short. A couple of them though are quite the character soup. This for example here is a single selector:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;VariableDeclaration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;not&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ExportNamedDeclaration &gt; .declaration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &gt; VariableDeclarator.&lt;span class=&quot;token property&quot;&gt;declarations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;  [init.type=&lt;span class=&quot;token string&quot;&gt;&quot;ArrayExpression&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;	[init.type=&lt;span class=&quot;token string&quot;&gt;&quot;CallExpression&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;[init.type=&lt;span class=&quot;token string&quot;&gt;&quot;NewExpression&quot;&lt;/span&gt;]&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;[init.optional!=true][init.callee.type=&lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;][init.callee.name=&lt;span class=&quot;token string&quot;&gt;&quot;Array&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;[init.type=&lt;span class=&quot;token string&quot;&gt;&quot;CallExpression&quot;&lt;/span&gt;][init.optional!=true][init.callee.type=&lt;span class=&quot;token string&quot;&gt;&quot;MemberExpression&quot;&lt;/span&gt;][init.callee.computed!=true][init.callee.property.type=&lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;][init.callee.optional!=true]&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;from&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;of&quot;&lt;/span&gt;]&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;[init.callee.object.type=&lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;][init.callee.object.name=&lt;span class=&quot;token string&quot;&gt;&quot;Array&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;[init.type=&lt;span class=&quot;token string&quot;&gt;&quot;CallExpression&quot;&lt;/span&gt;][init.optional!=true][init.callee.type=&lt;span class=&quot;token string&quot;&gt;&quot;MemberExpression&quot;&lt;/span&gt;][init.callee.computed!=true][init.callee.property.type=&lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;][init.callee.optional!=true]&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;concat&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;copyWithin&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;fill&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;filter&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;flat&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;flatMap&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;map&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;reverse&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;slice&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;sort&quot;&lt;/span&gt;]&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  	[init.callee.property.name=&lt;span class=&quot;token string&quot;&gt;&quot;splice&quot;&lt;/span&gt;]&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &gt; Identifier.id&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is certainly an example where I feel like we went a little off the rails. I don&#39;t want to be the person who has to debug that thing when it isn&#39;t matching correctly. That&#39;s my main gripe with any form of custom domain specific languages. They usually don&#39;t come with tooling support at all. If we stay in JavaScript-land instead we can inspect the value at any time at any step with a proper debugger. Whilst the previous string selector example is a bit extreme, the majority of selectors look like this:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;BinaryExpression&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;VariableDeclaration&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s it. Most of the selectors just want to know if the current AST node is of a certain type. Nothing more. For that we don&#39;t really need a whole selector engine. What if we introduced a fast path for that and bypassed the selector engine completely?&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NodeEventGenerator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	isType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token string&quot;&gt;&quot;IfStatement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token string&quot;&gt;&quot;BinaryExpression&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// ...etc&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;applySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// Fast path, just assert on type&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawSelector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; selector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawSelector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;emitter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawSelector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// Fallback to full selector engine matching&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;			esquery&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;				node&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;				selector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parsedSelector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentAncestry&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;esqueryOptions&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;emitter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;selector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rawSelector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since we&#39;re already short circuiting the selector engine, I became curious about how a stringified selector would fare against one written as a plain JavaScript function. My gut told me that a selector written as a simple JavaScript condition would be way easier to optimize for engines.&lt;/p&gt;
&lt;h2&gt;Rethinking selectors&lt;/h2&gt;
&lt;p&gt;A selector engine is very useful if you need to pass traversal commands across language barriers like we do with CSS in the browser. It never is free though, as a selector engine always needs to parse the selector to deconstruct what we should do, and then build some logic on the fly to execute that parsed thing.&lt;/p&gt;
&lt;p&gt;But inside &lt;code&gt;eslint&lt;/code&gt; we&#39;re not crossing any language barriers. We stay in JavaScript land. Therefore we don&#39;t gain anything performance-wise by converting query instructions into a selector and parsing them back into something we can run again. Rather, we consume about 25% of the total linting time for parsing and executing selectors. A new approach was needed.&lt;/p&gt;
&lt;p&gt;Then it hit me.&lt;/p&gt;
&lt;p&gt;A selector is conceptually nothing more than a &amp;quot;description&amp;quot; to get a find elements based on criterias it holds. That could be a lookup in a tree or a flat data structure like an &lt;code&gt;array&lt;/code&gt;. If you think about it, even the callback function in a standard &lt;code&gt;Array.prototype.filter()&lt;/code&gt; call is a selector. We&#39;re selecting values out of a collection of items (=Array) and picking only the values we care about. What we&#39;re doing with the &lt;code&gt;esquery&lt;/code&gt; is exactly the same thing. Out of a bunch of objects (=AST nodes), we&#39;re picking out the ones which match a certain criteria. That&#39;s a selector! So what if we avoided the selector parsing logic and used a plain JavaScript function instead?&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// String based esquery selector&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; esquerySelector &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[type=&quot;CallExpression&quot;][callee.type=&quot;MemberExpression&quot;][callee.computed!=true][callee.property.type=&quot;Identifier&quot;]:matches([callee.property.name=&quot;substr&quot;], [callee.property.name=&quot;substring&quot;])&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// The same selector as a plain JS function&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;jsSelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;		node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CallExpression&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;br /&gt;		node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;callee&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MemberExpression&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;callee&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;computed &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;br /&gt;		node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;callee&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;property&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;callee&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;property&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;substr&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;&lt;br /&gt;			node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;callee&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;property&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;substring&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s give this a go! I wrote a few benchmarks to measure the time difference of these two approaches. A little later and the data popped up on my screen.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;&lt;code&gt;foo.substr(1, 2)&lt;/code&gt; ops/sec&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;esquery&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;422,848.208&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;esquery (optimized)&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;3,036,384.255&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plain JavaScript function&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;66,961,066.5239&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Looks like the pure JavaScript function variant easily outperforms the string based one. It&#39;s vastly superior. Even after spending all this time on making &lt;code&gt;esquery&lt;/code&gt; faster, it&#39;s nowhere near the JavaScript variant. In cases where the selector doesn&#39;t match and the engine can bail out earlier it was still 30x slower than a plain function. This little experiment confirmed my assumption that we&#39;re paying quite of time for the selector engine.&lt;/p&gt;
&lt;h2&gt;The impact of third-party plugins and presets&lt;/h2&gt;
&lt;p&gt;Although there is much more room for optimizations visible in the profile from &lt;code&gt;eslint&lt;/code&gt;&#39;s setup, I began to wonder if I&#39;m spending time optimizing the right thing. Did the same issues we&#39;ve seen so far in &lt;code&gt;eslint&lt;/code&gt;&#39;s own linting setup occur in other linting setups too? One of the key strengths of &lt;code&gt;eslint&lt;/code&gt; has always been its flexibility and support for third party linting rules. Looking back, pretty much every project I worked on had a couple of custom linting rules and about 2-5 additional eslint plugins or presets installed. But what&#39;s more important is that they switched out the parser completely. A quick glance at the npm download stats highlights a trend in replacing &lt;code&gt;eslint&lt;/code&gt;&#39;s built-in parser:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;npm downloads (weekly)&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;%&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;eslint&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;31.718.905&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@typescript-eslint/parser&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;23.192.767&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;73%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@babel/eslint-parser&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;6.057.110&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;19%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If those numbers are to be believed, it would mean that only 8% of all &lt;code&gt;eslint&lt;/code&gt; users use the built-in parser. It also shows how ubiquitous TypeScript has become, making up the lionshare of eslint&#39;s total user base with 73%. We don&#39;t have data on whether users of the babel parser use it for TypeScript too. My guess would be that a portion of them do that and that the total number of TypeScript users is actually even higher.&lt;/p&gt;
&lt;p&gt;After profiling a few different setups in various open source repositories, I settled on the one from &lt;a href=&quot;https://github.com/vitejs/vite&quot;&gt;vite&lt;/a&gt; which contains a good bunch of the patterns that were present in other profiles too. Its codebase is written in TypeScript and &lt;code&gt;eslint&lt;/code&gt;&#39;s parser has been replaced accordingly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-eslint-vite.png&quot; alt=&quot;Screenshot of speedscope showing a profile of eslint running on the vite repo. TypeScript parsing and AST conversion consume the majority of the time.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Like before, we can make out various areas in the profile showing where the time is spent. There is an area that hints at a conversion from TypeScript&#39;s format to the one &lt;code&gt;eslint&lt;/code&gt; understands taking quite a bit of time. Something odd is happening with the configuration loading too, as it never should take up that much time as it does here. And we found an old friend, the &lt;code&gt;eslint-import-plugin&lt;/code&gt; and &lt;code&gt;eslint-plugin-node&lt;/code&gt; which seem to kickoff a bunch of module resolution logic.&lt;/p&gt;
&lt;p&gt;The interesting bit here though is that the overhead of the selector engine doesn&#39;t show up at. There are some instances of an &lt;code&gt;applySelector&lt;/code&gt; function being called, but it barely consumes any time in the bigger picture.&lt;/p&gt;
&lt;p&gt;The two third party plugins that always seem to pop up and take a considerable time to execute are &lt;code&gt;eslint-plugin-import&lt;/code&gt; and &lt;code&gt;eslint-plugin-node&lt;/code&gt;. Whenever one or both of these plugins were active, it really showed in the profiling data. Both cause heavy filesystem traffic as they try to resolve a bunch of modules, but don&#39;t cache the results. We wrote a lot about that in &lt;a href=&quot;https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-2/&quot;&gt;part 2&lt;/a&gt; of this series, so I won&#39;t go into more detail on that.&lt;/p&gt;
&lt;h2&gt;Converting all the AST nodes&lt;/h2&gt;
&lt;p&gt;We&#39;ll start with the TypeScript conversion happening at the beginning. Our tools parse the code we provide to them into a data structure that is known as an Abstract-Syntax-Tree, in short: AST. You can think of that as being the building blocks that all our tooling works with. It tells information like: &amp;quot;hey, here we declare a variable and it has this name and that value&amp;quot;, or &amp;quot;here is an if-statement with this condition, that guards that code-block&amp;quot;, etc.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// `const foo = 42` in AST form is something like:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;VariableDeclaration&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;const&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;declarations&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;VariableDeclarator&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Identifier&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;NumericLiteral&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can see for yourself how our tools parse code on the excellent &lt;a href=&quot;https://astexplorer.net/&quot;&gt;AST Explorer&lt;/a&gt; page. I highly recommend visiting that and playing around with various code snippets. It gives you a good understanding of how similar or often different the AST format of our tools are.&lt;/p&gt;
&lt;p&gt;However, in &lt;code&gt;eslint&lt;/code&gt;&#39;s case there is a problem with that. We want rules to work regardless of the parser we&#39;ve chosen. When we activate the &lt;code&gt;no-console&lt;/code&gt; rule we want it to work across all of them and not force each rule to be rewritten for every single parser out there. Essentially, what we need is a shared AST format that we all can agree on. And that&#39;s exactly what &lt;code&gt;eslint&lt;/code&gt; does. It expects every AST node to match the &lt;a href=&quot;https://github.com/estree/estree&quot;&gt;&lt;code&gt;estree&lt;/code&gt;&lt;/a&gt; spec which codifies how each AST node should look. It&#39;s a spec that has been around for quite a while and many JavaScript tools started out with. Even babel is build on that, but has a few &lt;a href=&quot;https://babeljs.io/docs/en/babel-parser#output&quot;&gt;documented deviations&lt;/a&gt; since then.&lt;/p&gt;
&lt;p&gt;But therein lies the crux of the problem when you&#39;re using TypeScript. TypeScript&#39;s AST format is very different as it also needs to account for nodes representing the types itself. Some constructs are represent differently internally too as it makes things easier for TypeScript itself. That means every single TypeScript AST node has to be converted to the format &lt;code&gt;eslint&lt;/code&gt; understands. That conversion takes time. In this profile that accounts for &lt;strong&gt;~22%&lt;/strong&gt; of the total time. The reason it takes so long is not just the traversal alone, but also that with every conversion we&#39;ll be allocating new objects. We basically have two copies in different AST formats in memory.&lt;/p&gt;
&lt;p&gt;Maybe babel&#39;s parser is faster? What if we swapped out &lt;code&gt;@typescript-eslint/parser&lt;/code&gt; with &lt;code&gt;@babel/eslint-parser&lt;/code&gt;?&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Parse time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@typescript-eslint/parser&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;2.1s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@babel/eslint-parser&lt;/code&gt; + &lt;code&gt;@babel/preset-typescript&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;0.6s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Turns out that doing just that saves us quite a bit of time. Interestingly, this change also cut down the config loading time by a good chunk. The improvement to the config load time is probably due to babel&#39;s parser being spread out in fewer files.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-eslint-parser-config.png&quot; alt=&quot;Screenshot of speedscope showing that the time to load the configuration went down from 833ms to 532ms&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Note that whilst the babel parser is (at the time of this writing) noticeably faster, it doesn&#39;t support type-aware linting. That&#39;s a feature exclusive to &lt;code&gt;@typescript-eslint/parser&lt;/code&gt;. This opens up the possibilities for rules like their &lt;a href=&quot;https://typescript-eslint.io/rules/no-for-in-array/&quot;&gt;&lt;code&gt;no-for-in-array&lt;/code&gt;&lt;/a&gt; rule, which can detect if the variable you&#39;re iterating over in a &lt;code&gt;for-in&lt;/code&gt; loop is actually an &lt;code&gt;object&lt;/code&gt; instead of an &lt;code&gt;array&lt;/code&gt;. So you might want to continue using &lt;code&gt;@typescript-eslint/parser&lt;/code&gt;. If you are confident that you don&#39;t use any of their rules though and you are just looking for &lt;code&gt;eslint&lt;/code&gt; to be able to understand TypeScript&#39;s syntax, plus be a little quicker to lint, then switching to babel&#39;s parser is a good option.&lt;/p&gt;
&lt;h2&gt;Bonus round: What would an ideal linter look like?&lt;/h2&gt;
&lt;p&gt;Around this point I stumbled upon &lt;a href=&quot;https://github.com/eslint/eslint/discussions/16557&quot;&gt;a discussion&lt;/a&gt; about the future of &lt;code&gt;eslint&lt;/code&gt;, with performance being one of the top priorities. There are some great ideas put forth in there, especially about introducing the notion of a session to allow for full program linting vs on a per file basis like it is done today. Given that around at least 73% of &lt;code&gt;eslint&lt;/code&gt; users use it to lint TypeScript code, a tighter integration that requires less AST conversion would also be huge in regards to performance.&lt;/p&gt;
&lt;p&gt;There is also some chatter about a rust port, which piqued my curiosity on how fast current rust-based linters for JavaScript are. The only one that seems to be somewhat production ready and able to parse a large portion of the TypeScript grammar is &lt;a href=&quot;https://github.com/rslint/rslint&quot;&gt;rslint&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Besides &lt;code&gt;rslint&lt;/code&gt;, I also began to wonder what a simple linter in pure JavaScript would look like. One that didn&#39;t have a selector engine, didn&#39;t require constant AST conversion, and just the necessities to parse code and check various rules on it. So I wrapped babel&#39;s parser with a very simple API and added custom traversal logic to walk the AST tree. I didn&#39;t pick babel&#39;s own traversal functions because they cause lots of allocations with every iteration and are built on generators which is a little slower than not using generators. Also tried it with a few custom JavaScript/TypeScript parsers that I wrote myself over the years which originated from porting esbuild&#39;s parser to JavaScript a couple of years back.&lt;/p&gt;
&lt;p&gt;With that said, here are the numbers when running them all on vite&#39;s repository (144 files).&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;eslint (JavaScript)&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;5.85s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom linter (JavaScript)&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;0.52s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rslint (rust based)&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;0.45s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Based on these numbers I&#39;m fairly confident that we can get very close to rust&#39;s performance with just JavaScript based on this little experiment.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Overall, the &lt;code&gt;eslint&lt;/code&gt; project has a very bright future ahead. It&#39;s one of the most successful OSS projects and it has found the secret of getting a good amount of funding. We looked at a couple of things that would make eslint faster, and there are lots more areas to look into that weren&#39;t covered here.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/eslint/eslint/discussions/16557&quot;&gt;&amp;quot;future of eslint&amp;quot; discussion&lt;/a&gt; contains a lot of great ideas that would make &lt;code&gt;eslint&lt;/code&gt; better and potentially faster. I think the tricky bit is avoiding trying to fix every problem at once as that&#39;s often doomed to fail in my experience. Same goes for a ground up rewrite. Instead, I think that the current code base is a perfectly good starting point, ready to be shaped into something even more awesome.&lt;/p&gt;
&lt;p&gt;From an outsiders perspective, there are some key decisions to make. Like, does it make sense at this point to keep supporting string based selectors? If yes, does the eslint team have the capacity to take on maintenance of &lt;code&gt;esquery&lt;/code&gt; and give it some much needed love? And what about native TypeScript support given that the npm download counts point at 73% of eslint users being TypeScript users?&lt;/p&gt;
&lt;p&gt;Whatever will happen, I have strong confidence in their team and the ability to execute their vision. I&#39;m excited for the future of &lt;code&gt;eslint&lt;/code&gt;!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Speeding up the JavaScript ecosystem - module resolution</title>
    <link href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-2/"/>
    <updated>2023-01-15T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-2/</id>
    <content type="html">&lt;p&gt;In &lt;a href=&quot;https://marvinh.dev/blog/speeding-up-javascript-ecosystem/&quot;&gt;part 1&lt;/a&gt; of this series we found a few ways to speed various libraries used in JavaScript tools. Whilst those low level patches moved the total build time number by a good chunk, I was wondering if there is something more fundamental in our tooling that can be improved. Something that has a greater impact on the total time of common JavaScript tasks like bundling, testing and linting.&lt;/p&gt;
&lt;p&gt;So over the next couple of days I collected about a dozen CPU profiles from various tasks and tools that are commonly used in our industry. After a bit of inspection, I came across a repeating pattern that was present in every profile I looked at and affected the total runtime of these tasks by as much as 30%. It’s such a critical and influential part of our infrastructure that it deserves to have its own blog post.&lt;/p&gt;
&lt;p&gt;That critical piece is called module resolution. And in all the traces I looked at it took more time in total than parsing source code.&lt;/p&gt;
&lt;h2&gt;The cost of capturing stack traces&lt;/h2&gt;
&lt;p&gt;It all started when I noticed that the most time consuming aspect in those traces was spent in &lt;code&gt;captureLargerStackTrace&lt;/code&gt; an internal node function responsible for attaching stack traces to &lt;code&gt;Error&lt;/code&gt; objects. That seemed a bit out of the ordinary, given that both tasks succeeded without showing any signs of errors being thrown.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-2-trace.png&quot; alt=&quot;Result table in speedscope showing captureLargerStack traces taking up to 29% time in various traces&quot; /&gt;&lt;/p&gt;
&lt;p&gt;After clicking through a bunch of occurrences in the profiling data a clearer picture emerged as to what was happening. Nearly all of the error creations came from calling node’s native &lt;a href=&quot;https://nodejs.org/api/fs.html#fsfstatsyncfd-options&quot;&gt;&lt;code&gt;fs.statSync()&lt;/code&gt;&lt;/a&gt; function and that in turn was called inside a function called &lt;code&gt;isFile&lt;/code&gt;. The documentation mentions that &lt;code&gt;fs.statSync()&lt;/code&gt; is basically the equivalent to POSIX’s &lt;code&gt;fstat&lt;/code&gt; command and commonly used to check if a path exists on disk, is a file or a directory. With that in mind we should only get an error here in the exceptional use case when the file doesn’t exist, we lack permissions to read it or something similar. It was time to take a peek at the source of &lt;code&gt;isFile&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; stat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; stat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; stat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isFIFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;code &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ENOENT&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;code &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ENOTDIR&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From a quick glance it’s an innocent looking function, but was showing up in traces nonetheless. Noticeably, we ignore certain error cases and return &lt;code&gt;false&lt;/code&gt; instead of forwarding the error. Both the &lt;code&gt;ENOENT&lt;/code&gt; and &lt;code&gt;ENOTDIR&lt;/code&gt; error codes ultimately mean that the path doesn’t exist on disk. Maybe that’s the overhead we’re seeing? I mean we’re immediately ignoring those errors here. To test that theory I logged out all the errors that the try/catch-block caught. Low and behold every single error that was thrown was either a &lt;code&gt;ENOENT&lt;/code&gt; code or an &lt;code&gt;ENOTDIR&lt;/code&gt; code.&lt;/p&gt;
&lt;p&gt;A peek into node’s &lt;a href=&quot;https://nodejs.org/api/fs.html#fsstatsyncpath-options&quot;&gt;documentation of &lt;code&gt;fs.statSync&lt;/code&gt;&lt;/a&gt; reveals that it supports passing a &lt;code&gt;throwIfNoEntry&lt;/code&gt; option that prevents errors from being thrown when no file system entry exists. Instead it will return &lt;code&gt;undefined&lt;/code&gt; in that case.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; stat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; throwIfNoEntry&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; stat &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; stat&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isFIFO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Applying that option allows us to get rid of the if-statment in the catch block which in turn makes the try/catch redundant and allows us to simplify the function even further.&lt;/p&gt;
&lt;p&gt;This single change reduced the time to lint the project by 7%. What’s even more awesome is that tests got a similar speedup from the same change too.&lt;/p&gt;
&lt;h2&gt;The file system is expensive&lt;/h2&gt;
&lt;p&gt;With the overhead of stack traces of that function being eliminated, I felt like there was still more to it. You know, throwing a couple of errors shouldn’t really show up at all in traces captured over the span of a couple of minutes. So I injected a simple counter into that function to get an idea how frequently it was called. It became apparent that it was called about 15k times, about 10x more than there were files in the project. That smells like an opportunity for improvement.&lt;/p&gt;
&lt;h3&gt;To module or not to module, that is the question&lt;/h3&gt;
&lt;p&gt;By default there are three kind of specifiers for a tool to know about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Relative module imports: &lt;code&gt;./foo&lt;/code&gt;, &lt;code&gt;../bar/boof&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Absolute module imports: &lt;code&gt;/foo&lt;/code&gt;, &lt;code&gt;/foo/bar/bob&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Package imports &lt;code&gt;foo&lt;/code&gt;, &lt;code&gt;@foo/bar&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most interesting of the three from a performance perspective is the last one. Bare import specifiers, the ones that don’t start with a dot &lt;code&gt;.&lt;/code&gt; or with a slash &lt;code&gt;/&lt;/code&gt;, are a special kind of import that typically refer to npm packages. This algorithm is described in depth in &lt;a href=&quot;https://nodejs.org/api/esm.html#resolution-algorithm&quot;&gt;node’s documentation&lt;/a&gt;. The gist of it is that it tries to parse the package name and then it will traverse upwards to check if a special &lt;code&gt;node_modules&lt;/code&gt; directory is present that contains the module until it reaches the root of the file system. Let’s illustrate that with an example.&lt;/p&gt;
&lt;p&gt;Let’s say that we have a file located at &lt;code&gt;/Users/marvinh/my-project/src/features/DetailPage/components/Layout/index.js&lt;/code&gt; that tries to import a module &lt;code&gt;foo&lt;/code&gt;. The algorithm will then check for the following locations.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/Users/marvinh/my-project/src/features/DetailPage/components/Layout/node_modules/foo/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/Users/marvinh/my-project/src/features/DetailPage/components/node_modules/foo/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/Users/marvinh/my-project/src/features/DetailPage/node_modules/foo/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/Users/marvinh/my-project/src/features/node_modules/foo/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/Users/marvinh/my-project/src/node_modules/foo/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/Users/marvinh/my-project/node_modules/foo/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/Users/marvinh/node_modules/foo/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/Users/node_modules/foo/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s a lot of file system calls. In a nutshell every directory will be checked if it contains a module directory. The amount of checks directly correlates to the number of directories the importing file is in. And the problem is that this happens for every file where &lt;code&gt;foo&lt;/code&gt; is imported. Meaning if &lt;code&gt;foo&lt;/code&gt; is imported in a file residing somewhere else, we’ll crawl the whole directory tree upwards again until we find a &lt;code&gt;node_modules&lt;/code&gt; directory that contains the module. And that’s an aspect where caching the resolved module greatly helps.&lt;/p&gt;
&lt;p&gt;But it gets even better! Lots of projects make use of path mapping aliases to save a little bit of typing, so that you can use the same import specifiers everywhere and avoid lots of dots &lt;code&gt;../../../&lt;/code&gt;. This is typically done via TypeScript’s &lt;code&gt;paths&lt;/code&gt; compiler option or a resolve alias in a bundler. The problem with that is that these typically are indistinguishable from package imports. If I add a path mapping to the features directory at &lt;code&gt;/Users/marvinh/my-project/src/features/&lt;/code&gt; so that I can use an import declaration like &lt;code&gt;import {...} from “features/DetailPage”&lt;/code&gt;, then every tool should know about this.&lt;/p&gt;
&lt;p&gt;But what if it doesn’t? Since there is no centralized module resolution package that every JavaScript tool uses, they are multiple competing ones with various levels of features supported. In my case the project makes heavy use of path mappings and it included a linting plugin that wasn’t aware of the path mappings defined in TypeScript’s &lt;code&gt;tsconfig.json&lt;/code&gt;. Naturally, it assumed that &lt;code&gt;features/DetailPage&lt;/code&gt; was referring to a node module, which led it to do the whole recursive upwards traversal dance in hopes of finding the module. But it never did, so it threw an error.&lt;/p&gt;
&lt;h2&gt;Caching all the things&lt;/h2&gt;
&lt;p&gt;Next I enhanced the logging to see how many unique file paths the function was called with and if it always returned the same result. Only about 2.5k calls to &lt;code&gt;isFile&lt;/code&gt; had a unique file path and there was a strong 1:1 mapping between the passed file argument and the returned value. It’s still more than the amount of files in the project, but it’s much lower than the total 15k times it was called. What if we added a cache around that to avoid reaching out to the file system?&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cached &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cached &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cached&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ...existing resolution logic here&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; resolved &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resolved&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The addition of a cache sped up the total linting time by another 15%. Not bad! The risky bit about caching though is that they might become stale. There is a point in time where they usually have to be invalidated. Just to be on the safe side I ended up picking a more conservative approach that checks if the cached file still exists. This is not an uncommon thing to happen if you think of tooling often being run in watch mode where it’s expected to cache as much as possible and only invalidate the files that changed.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cached &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// A bit conservative: Check if the cached file still exists on disk to avoid&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// stale caches in watch mode where a file could be moved or be renamed.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cached &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cached&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ...existing resolution logic here&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ext &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; extensions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; filePath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; file &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; ext&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filePath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; filePath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; filePath&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Could not resolve &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;file&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was honestly expecting it to nullify the benefits of adding a cache in the first place since we’re reaching to the file system even in the cached scenario. But looking at the numbers this only worsened the total linting time only by 0.05%. That’s a very minor hit in comparison, but shouldn’t the additional file system call matter more?&lt;/p&gt;
&lt;h2&gt;The file extension guessing game&lt;/h2&gt;
&lt;p&gt;The thing with modules in JavaScript is that the language didn’t have a module system from the get go. When node.js came onto the scene it popularized the CommonJS module system. That system has several “cute” features like the ability to omit the extension of the file you’re loading. When you write a statement like &lt;code&gt;require(&amp;quot;./foo&amp;quot;)&lt;/code&gt; it will automatically add the &lt;code&gt;.js&lt;/code&gt; extension and try to read the file at &lt;code&gt;./foo.js&lt;/code&gt;. If that isn’t present it will check for json file &lt;code&gt;./foo.json&lt;/code&gt; and if that isn’t available either, it will check for an index file at &lt;code&gt;./foo/index.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Effectively we’re dealing with ambiguity here and the tooling has to make sense of what &lt;code&gt;./foo&lt;/code&gt; should resolve to. With that there is a high chance of doing wasted file system calls as there is no way of knowing where to resolve the file to, in advance. Tools literally have to try each combination until they find a match. This is worsened if we look at the total amount of possible extensions that exist today. Tools typically have an array of potential extensions to check for. If you include TypeScript the full list for a typical frontend project at the time of this writing is:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; extensions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;.jsx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;.cjs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;.mjs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;.tsx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;.mts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;.cts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s 8 potential extensions to check for. And that’s not all. You essentially have to double that list to account for index files which could resolve to all those extensions too! This means that our tools have no other option, other than looping through the list of extensions until we find one that exists on disk. When we want to resolve &lt;code&gt;./foo&lt;/code&gt; and the actual file is &lt;code&gt;foo.ts&lt;/code&gt;, we’d need to check:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;foo.js&lt;/code&gt; -&amp;gt; doesn’t exist&lt;/li&gt;
&lt;li&gt;&lt;code&gt;foo.jsx&lt;/code&gt; -&amp;gt; doesn’t exist&lt;/li&gt;
&lt;li&gt;&lt;code&gt;foo.cjs&lt;/code&gt; -&amp;gt; doesn’t exist&lt;/li&gt;
&lt;li&gt;&lt;code&gt;foo.mjs&lt;/code&gt; -&amp;gt; doesn’t exist&lt;/li&gt;
&lt;li&gt;&lt;code&gt;foo.ts&lt;/code&gt; -&amp;gt; bingo!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s four unnecessary file system calls. Sure you could change the order of the extensions and put the most common ones in your project at the start of the array. That would increase the chances of the correct extension to be found earlier, but it doesn’t eliminate the problem entirely.&lt;/p&gt;
&lt;p&gt;As part of the ES2015 spec a new module system was proposed. All the details weren’t fleshed out in time, but the syntax was. Import statements quickly took over as they have very benefits over CommonJS for tooling. Due to its staticness it opened up the space for lots more tooling enhanced features like most famously tree-shaking where unused modules and or even functions in modules can be easily detected and dropped from the production build. Naturally, everyone jumped on the new import syntax.&lt;/p&gt;
&lt;p&gt;There was one problem though: Only the syntax was finalized and not how the actual module loading or resolution should work. To fill that gap, tools re-used the existing semantics from CommonJS. This was good for adoption as porting most code bases only required syntactical changes and these could be automated via codemods. This was a fantastic aspect from an adoption point of view! But that also meant that we inherited the guessing game of which file extension the import specifier should resolve to.&lt;/p&gt;
&lt;p&gt;The actual spec for module loading and resolution was finalized years later and it corrected this mistake by making extensions mandatory.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Invalid ESM, missing extension in import specifier&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; doSomething &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Valid ESM&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; doSomething &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./foo.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By removing this source of ambiguity and always adding an extension, we’re avoiding an entire class of problems. Tools get way faster too. But it will take time until the ecosystem moves forward on that or if at all, since tools have adapted to deal with the ambiguity.&lt;/p&gt;
&lt;h2&gt;Where to go from here?&lt;/h2&gt;
&lt;p&gt;Throughout this whole investigation I was a bit surprised to find that much room for improvement in regards to optimizing module resolution, given that it’s such a central in our tools. The few changes described in this article reduced the linting times by &lt;strong&gt;30%&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;The few optimizations we did here are not unique to JavaScript either. Those are the same optimizations that can be found in toolings for other programming languages. When it comes to module resolution the four main takeaways are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Avoid calling out to the file system as much as possible&lt;/li&gt;
&lt;li&gt;Cache as much as you can to avoid calling out to the file system&lt;/li&gt;
&lt;li&gt;When you&#39;re using &lt;code&gt;fs.stat&lt;/code&gt; or &lt;code&gt;fs.statSync&lt;/code&gt; always set the &lt;code&gt;throwIfNoEntry: false&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Limit upwards traversal as much as possible&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The slowness in our tooling wasn’t caused by JavaScript the language, but by things just not being optimized at all. The fragmentation of the JavaScript ecosystem doesn&#39;t help either as there isn’t a single standard package for module resolution. Instead, there are multiple and they all share a different subset of features. That’s no surprise though as the list of features to support &lt;a href=&quot;https://twitter.com/devongovett/status/1609795073432068096&quot;&gt;has grown over the years&lt;/a&gt; and there is no single library out there that supports them all at the time of this writing. Having a single library that everyone uses would make solving this problem once and for all for everyone a lot easier.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Speeding up the JavaScript ecosystem - one library at a time</title>
    <link href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem/"/>
    <updated>2022-11-29T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/speeding-up-javascript-ecosystem/</id>
    <content type="html">&lt;p&gt;Whilst the trend is seemingly to rewrite every JavaScript build tool in other languages such as Rust or Go, the current JavaScript-based tools could be a lot faster. The build pipeline in a typical frontend project is usually composed of many different tools working together. But the diversification of tools makes it a little harder to spot performance problems for tooling maintainers as they need to know which tools their own ones are frequently used with.&lt;/p&gt;
&lt;p&gt;Although JavaScript is certainly slower than Rust or Go from a pure language point of view, the current JavaScript tools could be improved considerably. Sure JavaScript is slower, but it shouldn&#39;t be &lt;em&gt;that&lt;/em&gt; slow in comparison as it is today. JIT engines are crazy fast these days!&lt;/p&gt;
&lt;p&gt;Curiosity lead me down the path of spending some time profiling common JavaScript-based tools to kinda see where all that time was spent. Let&#39;s start with PostCSS, a very popular parser and transpiler for anything CSS.&lt;/p&gt;
&lt;h2&gt;Saving 4.6s in PostCSS&lt;/h2&gt;
&lt;p&gt;There is a very useful plugin called &lt;a href=&quot;https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-custom-properties&quot;&gt;postcss-custom-properties&lt;/a&gt; that adds basic support for CSS Custom Properties in older browsers. Somehow it showed up very prominently in traces with a costly 4.6s being attributed to a single regex that it uses internally. That looked odd.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-postcss-custom-properties.png&quot; alt=&quot;Picture of a flamegraph showing that the regex alone takes 4.6s&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The regex looks suspiciously like something that searches for a particular comment value to change the plugin&#39;s behaviour, similar to the ones from &lt;a href=&quot;https://eslint.org/&quot;&gt;eslint&lt;/a&gt; used to disable specific linting rules. It&#39;s not mentioned in their README, but a peek into the source code confirmed that assumption.&lt;/p&gt;
&lt;p&gt;The place where the regex is created is part of a &lt;a href=&quot;https://github.com/csstools/postcss-plugins/blob/main/plugins/postcss-custom-properties/src/lib/is-ignored.ts#L1-L6&quot;&gt;function that checks&lt;/a&gt; if a CSS rule or declaration is preceeded by said comment.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isBlockIgnored&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;ruleOrDeclaration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rule &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ruleOrDeclaration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selector&lt;br /&gt;		&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; ruleOrDeclaration&lt;br /&gt;		&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ruleOrDeclaration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;(!&#92;s*)?postcss-custom-properties:&#92;s*off&#92;b&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;i&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rule&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;rule.toString()&lt;/code&gt; call caught my eye pretty quickly. Places where one type is cast to another are usually worth another look if you&#39;re tackling performance as not having to do the conversion always saves time. What was interesting in this scenario is that the &lt;code&gt;rule&lt;/code&gt; variable always holds an &lt;code&gt;object&lt;/code&gt; with a custom &lt;code&gt;toString&lt;/code&gt; method. It was never a string to begin with, so we know that we&#39;re always paying a bit of serialization cost here to be able to test the regex against. From experience I knew that matching a regex against many short strings is a lot slower than matching it against few long ones. This is a prime candidate waiting to be optimized!&lt;/p&gt;
&lt;p&gt;The rather troubling aspect of this code is that every input file has to pay this cost, regardless of whether it has a postcss comment or not. Knowing that running one regex over a long string is cheaper than the repeated regex over short strings and the serialization cost, we can guard this function to avoid even having to call &lt;code&gt;isBlockIgnored&lt;/code&gt; if we know that the file doesn&#39;t contain any postcss comments.&lt;/p&gt;
&lt;p&gt;With &lt;a href=&quot;https://github.com/csstools/postcss-plugins/pull/730#issuecomment-1328120939&quot;&gt;the fix&lt;/a&gt; applied, the build time went down by a whopping &lt;strong&gt;4.6s&lt;/strong&gt;!&lt;/p&gt;
&lt;h2&gt;Optimizing SVG compression speed&lt;/h2&gt;
&lt;p&gt;Next up is &lt;a href=&quot;https://github.com/svg/svgo&quot;&gt;SVGO&lt;/a&gt;, a library for compressing SVG files. It&#39;s pretty awesome and a staple for projects with lots of SVG icons. The CPU profile reveiled that &lt;strong&gt;3.1s&lt;/strong&gt; was spent in compressing SVGs though. Can we speed that up?&lt;/p&gt;
&lt;p&gt;Searching a bit through the profiling data there was one function that stood out: &lt;code&gt;strongRound&lt;/code&gt;. What&#39;s more is that function was always followed by a little bit of GC cleanup shortly after (see the small red boxes).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-strongRound.png&quot; alt=&quot;Flamegraph showing lot of overhead caused by a strongRound function&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Consider my curiosity piqued! Let&#39;s pull up the &lt;a href=&quot;https://github.com/svg/svgo/blob/ae32acf5144be318872a45f49b3ece50c0a4cb18/plugins/convertPathData.js#L962&quot;&gt;source on GitHub&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br /&gt; * Decrease accuracy of floating-point numbers&lt;br /&gt; * in path data keeping a specified number of decimals.&lt;br /&gt; * Smart rounds values like 2.3491 to 2.35 instead of 2.349.&lt;br /&gt; */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strongRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;precision&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; rounded &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;precision &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rounded &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;precision &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; error&lt;br /&gt;					&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;precision&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rounded&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aha, so it&#39;s a function that is used to compress numbers, which there are a lot of in any typical SVG file. The function receives an array of &lt;code&gt;numbers&lt;/code&gt; and is expected to mutate its entries. Let&#39;s take a look at the type of variables that are used in its implementation. With a bit of closer inspection we notice that there is a lot of back and forth casting between string and numbers.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strongRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// Comparison between string and number -&gt; string is cast to number&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;precision&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token comment&quot;&gt;// Creating a string from a number that&#39;s casted immediately&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token comment&quot;&gt;// back to a number&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; rounded &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;precision &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token comment&quot;&gt;// Another number that is casted to a string and directly back&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token comment&quot;&gt;// to a number again&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rounded &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;precision &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; error&lt;br /&gt;					&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// This is the same value as in the if-condition before,&lt;/span&gt;&lt;br /&gt;					  &lt;span class=&quot;token comment&quot;&gt;// just casted to a number again&lt;/span&gt;&lt;br /&gt;					  &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;precision&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rounded&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The act of rounding numbers seems like something that can be done with a little bit of math alone, without having to convert numbers to strings. As a general rule of thumb a good chunk of optimizations are about expressing things in numbers, the main reason being that CPUs are crazy good at working with numbers. With a few changes here and there we can ensure that we always stay in number-land and thus avoid the string casting entirely.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Does the same as `Number.prototype.toFixed` but without casting&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// the return value to a string.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;num&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; precision&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pow &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt; precision&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; pow&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; pow&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Rewritten to get rid of all the string casting and call our own&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// toFixed() function instead.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strongRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fixed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; precision&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// Look ma, we can now use a strict equality comparison!&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fixed &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; rounded &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; precision &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;abs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rounded &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; precision &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; error&lt;br /&gt;					&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; fixed &lt;span class=&quot;token comment&quot;&gt;// We can now reuse the earlier value here&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; rounded&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running the profiling again confirmed that we were able to speed up build times by about &lt;strong&gt;1.4s&lt;/strong&gt;! I&#39;ve filed a &lt;a href=&quot;https://github.com/svg/svgo/pull/1716&quot;&gt;PR upstream&lt;/a&gt; for that too.&lt;/p&gt;
&lt;h3&gt;Regexes on short strings (part 2)&lt;/h3&gt;
&lt;p&gt;In close vicinity to &lt;code&gt;strongRound&lt;/code&gt; another function looked suspicious as it takes up nearly a whole second (0.9s) to complete.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-stringifyNumber.png&quot; alt=&quot;Flamegraph highlight usages of stringifyNumbers in the profile&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Similar to &lt;code&gt;stringRound&lt;/code&gt; this function compresses numbers too, but with the added trick that we can drop the leading zero if the number has decimals and is smaller than 1 and bigger than -1. So &lt;code&gt;0.5&lt;/code&gt; can be compressed to &lt;code&gt;.5&lt;/code&gt; and &lt;code&gt;-0.2&lt;/code&gt; to &lt;code&gt;-.2&lt;/code&gt; respectively. In particular the &lt;a href=&quot;https://github.com/svg/svgo/blob/ae32acf5144be318872a45f49b3ece50c0a4cb18/lib/path.js#L246&quot;&gt;last line&lt;/a&gt; looks of interest.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;stringifyNumber&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;precision&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ...snip&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// remove zero whole from decimal number&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;^0&#92;.&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;^-0&#92;.&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we are converting a number to a string and calling a regex on it. It&#39;s extremely likely that the string version of the number will be a short string. And we know that a number can not be both &lt;code&gt;n &amp;gt; 0 &amp;amp;&amp;amp; n &amp;lt; 1&lt;/code&gt; and &lt;code&gt;n &amp;gt; -1 &amp;amp;&amp;amp; n &amp;lt; 0&lt;/code&gt; at the same time. Not even &lt;code&gt;NaN&lt;/code&gt; has that power! From that we can deduce that either only one of the regexes matches or none of them, but never both. At least one of the &lt;code&gt;.replace&lt;/code&gt; calls is always wasted.&lt;/p&gt;
&lt;p&gt;We can optimize that by differentiating between those cases by hand. Only if we know that we&#39;re dealing with a number that has a leading &lt;code&gt;0&lt;/code&gt; should we apply our replacement logic. Those number checks are quicker than doing a regex search.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;stringifyNumber&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;precision&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ...snip&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// remove zero whole from decimal number&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; strNum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Use simple number checks&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; strNum&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;^0&#92;.&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; strNum&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-source language-regex&quot;&gt;^-0&#92;.&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; strNum&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can go one step further and get rid of the regex searches entirely as we know with 100% certainty where the leading &lt;code&gt;0&lt;/code&gt; is in the string and thus can manipulate the string directly.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;stringifyNumber&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;precision&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ...snip&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// remove zero whole from decimal number&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; strNum &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// Plain string processing is all we need&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; strNum&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; num &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// Plain string processing is all we need&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; strNum&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; strNum&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since there is a separate function to trim the leading &lt;code&gt;0&lt;/code&gt; in svgo&#39;s codebase already, we can leverage that instead. Another 0.9s saved! &lt;a href=&quot;https://github.com/svg/svgo/pull/1717&quot;&gt;Upstream PR&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Inline functions, inline caches and recursion&lt;/h3&gt;
&lt;p&gt;One function called &lt;code&gt;monkeys&lt;/code&gt; intrigued me due to its name alone. In traces I could see that it was called multiple times inside itself, which is a strong indicator that some sort of recursion is happening here. It&#39;s often used to traverse a tree-like structure. Whenever some sort of traversal is used, there is a likelihood that it&#39;s somewhat in the &amp;quot;hot&amp;quot; path of the code. That&#39;s not true for every scenario but in my experience it has been a good rule of thumb.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;perItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; info&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plugin&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;monkeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token comment&quot;&gt;// reverse pass&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reverse &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token function&quot;&gt;monkeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token comment&quot;&gt;// main filter&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; kept &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;plugin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				kept &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; plugin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; info&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token comment&quot;&gt;// direct pass&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;reverse &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token function&quot;&gt;monkeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; kept&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; items&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;monkeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we have a function that creates another function inside its body which recalls the inner function again. If I had to guess, I&#39;d assume that this was done here to save some keystrokes by not having to pass around all arguments again. Thing is that functions that are created inside other functions are pretty difficult to optimize when the outer function is called frequently.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;perItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;items&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; info&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plugin&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reverse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// reverse pass&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reverse &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token function&quot;&gt;perItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; info&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plugin&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reverse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// main filter&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; kept &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;plugin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;active&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			kept &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; plugin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; info&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// direct pass&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;reverse &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token function&quot;&gt;perItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; info&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plugin&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reverse&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; kept&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; items&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can get rid of the inner function by always passing all arguments explicitly vs capturing them by closure like before. The impact of this change is rather minor, but in total it saved another &lt;strong&gt;0.8s&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Luckily this is already addressed in the new major &lt;code&gt;3.0.0&lt;/code&gt; release, but it will take a bit until the ecosystem switches to the new version.&lt;/p&gt;
&lt;h2&gt;Beware of for...of transpilation&lt;/h2&gt;
&lt;p&gt;A nearly identical problem occurs in &lt;code&gt;@vanilla-extract/css&lt;/code&gt;. The published package ships with the following piece of code:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConditionalRuleset&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;getSortedRuleset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;_loop&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;_loop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;query&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dependents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dependents&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;precedenceLookup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token function&quot;&gt;_loop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dependents&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What&#39;s interesting about this function is that it&#39;s not present in the original source code. In the original source it&#39;s a standard &lt;code&gt;for...of&lt;/code&gt; loop.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConditionalRuleset&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;getSortedRuleset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;query&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dependents&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;precedenceLookup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token function&quot;&gt;doSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I couldn&#39;t replicate this issue in babel or typescript&#39;s repl, but I can confirm that it&#39;s introduced by their build pipeline. Given that it seems to be a shared abstraction over their build tool I&#39;d assume that a few more projects are affected by this. So for now I just patched the package locally inside &lt;code&gt;node_modules&lt;/code&gt; and was happy to see that this improved the build times by another &lt;code&gt;0.9s&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The curious case of semver&lt;/h2&gt;
&lt;p&gt;With this one I&#39;m not sure if I&#39;ve configured something wrong. Essentially, the profile showed that the whole babel configuration was always read anew whenever it transpiled a file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/js-tools-babel-semver.png&quot; alt=&quot;Flamegraph showing a long time being spent in parsing browser targets&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It&#39;s a bit hard to see in the screenshot, but one of the functions taking up much time was code from the &lt;code&gt;semver&lt;/code&gt; package, the same package that&#39;s used in npm&#39;s cli. Huh? What has semver to do with babel? It took a while until it dawned on me: It&#39;s for parsing the browserlist target for &lt;code&gt;@babel/preset-env&lt;/code&gt;. Although the browserlist settings might look pretty short, ultimately they were expanded to about 290 individual targets.&lt;/p&gt;
&lt;p&gt;That alone isn&#39;t enough for concern, but it&#39;s easy to miss the allocation cost when using validation functions. It&#39;s a bit spread out in babel&#39;s code base, but essentially the versions of the browser targets are converted to semver strings &lt;code&gt;&amp;quot;10&amp;quot; -&amp;gt; &amp;quot;10.0.0&amp;quot;&lt;/code&gt; and then validated. Some of those version numbers already match the semver format. These versions and sometimes version ranges are compared against each other until we find the lowest common feature set we need to transpile for. There is nothing wrong with this approach.&lt;/p&gt;
&lt;p&gt;Performance problems arise here, because the semver versions are stored as a &lt;code&gt;string&lt;/code&gt; instead of the parsed semver data type. This means that every call to &lt;code&gt;semver.valid(&#39;1.2.3&#39;)&lt;/code&gt; will create a new semver instance and immediately destroy it. Same is true when comparing semver versions when using strings: &lt;code&gt;semver.lt(&#39;1.2.3&#39;, &#39;9.8.7&#39;)&lt;/code&gt;. And &lt;em&gt;that&lt;/em&gt; is why we&#39;re seeing semver so prominently in the traces.&lt;/p&gt;
&lt;p&gt;By patching that locally in &lt;code&gt;node_modules&lt;/code&gt; again, I was able to reduce the build time by another &lt;code&gt;4.7s&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;At this point I stopped looking, but I&#39;d assume that you&#39;ll find more of these minor performance issues in popular libraries. Today we mainly looked at some build tools, but UI components or other libraries usually have the same low hanging performance issues.&lt;/p&gt;
&lt;p&gt;Will this be enough to match Go&#39;s or Rust&#39;s performance? Unlikely, but the thing is that the current JavaScript tools could be faster than they are today. And the things we looked at in this post are more or less just the tip of the iceberg.&lt;/p&gt;
&lt;p&gt;Continue reading &lt;a href=&quot;https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-2/&quot;&gt;Part 2&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Running 1000 tests in 1s</title>
    <link href="https://marvinh.dev/blog/running-1000-test-in-1s/"/>
    <updated>2022-10-02T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/running-1000-test-in-1s/</id>
    <content type="html">&lt;p&gt;Recently, I &lt;a href=&quot;https://twitter.com/marvinhagemeist/status/1575040852761915392&quot;&gt;tweeted&lt;/a&gt; that the whole test suite for &lt;a href=&quot;https://preactjs.com/&quot;&gt;Preact&lt;/a&gt;, a modern framework to build web apps, runs in approximately 1s. It&#39;s composed of 1003 tests at the time of this writing that run in the browser and assert against the real DOM.&lt;/p&gt;
&lt;p&gt;Here is a video of what that looks like:&lt;/p&gt;
&lt;video controls=&quot;&quot;&gt;
  &lt;source src=&quot;https://marvinh.dev/media/preact-tests.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;
&lt;p&gt;That tweet struck a nerve, because those numbers aren&#39;t even close to be achievable in any of the popular test runners in the frontend space right now. Many newer web developers have never experienced a fast test suite that runs in 1s. Naturally, the question popped up as to what it is that made Preact&#39;s test suite so fast.&lt;/p&gt;
&lt;h2&gt;Establishing a baseline&lt;/h2&gt;
&lt;p&gt;Before we begin, we should have a rough baseline of how fast it could be in theory. To do that we need to know what kind of tests we want to run. In Preact&#39;s case it&#39;s mostly about asserting that it rendered the correct output. For that we prefer to test against the real DOM inside one or potentially many browsers to ensure compatibility. The majority of test cases look similar to this:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Create a DOM container element to render into&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Render `&amp;lt;p&gt;foo&amp;lt;/p&gt;` into `&amp;lt;div id=&quot;app /&gt;`&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Check if the rendered HTML is correct.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;p&gt;foo&amp;lt;/p&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s simplify this even further so that we could copy and paste this snippet into the browser. And you know what: We&#39;ll flex the browser&#39;s muscle a little and run this test 100000 times.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; h &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://esm.sh/preact&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100_000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;p&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;p&gt;foo&amp;lt;/p&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fail&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;timeEnd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Result:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/test-runner-browser-time.png&quot; alt=&quot;asd&quot; /&gt;&lt;/p&gt;
&lt;p&gt;On my machine (MacBook Air M1) all 100000 tests complete in about &lt;code&gt;~182ms&lt;/code&gt;. Sure, the test case is a bit simplified and we definitely have a few more complex tests in our suite, but these timings are what we should keep in the back of our minds as a baseline of how fast things could be. Anything that we add to our setup from this point on is merely overhead.&lt;/p&gt;
&lt;h2&gt;Blast from the past&lt;/h2&gt;
&lt;p&gt;Those that have been around for a while, know that we the most popular test runners were different back in the day. There was &lt;a href=&quot;https://mochajs.org/&quot;&gt;Mocha&lt;/a&gt;, &lt;a href=&quot;https://jasmine.github.io/&quot;&gt;Jasmine&lt;/a&gt;, &lt;a href=&quot;https://node-tap.org/&quot;&gt;tap&lt;/a&gt;, &lt;a href=&quot;https://github.com/ljharb/tape&quot;&gt;tape&lt;/a&gt; and many more. What they all had in common was speed. Let&#39;s check that for ourselves by adding Mocha to the mix. A few script tags later after hitting refresh we get some a new number:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;~210ms&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Adding Mocha added about &lt;code&gt;~35ms&lt;/code&gt; of overhead. That&#39;s plenty fast for a test runner that has been around since 2011. Jasmine is even older, being published in 2008 and runs similarly quick.&lt;/p&gt;
&lt;p&gt;This poses the question: Why are those older test runners so much faster?&lt;/p&gt;
&lt;h2&gt;Living in an isolated world&lt;/h2&gt;
&lt;p&gt;The answer to that question is: Test isolation. With the rise of &lt;code&gt;Promises&lt;/code&gt; and later &lt;code&gt;async/await&lt;/code&gt;, writing asynchronous code became more popular and with that the drive for test runners to invest into test isolation. With every test having their own environment they can reliably ensure that no test interferes with another by mutating globals or other forms of shared state.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Module has a global variable&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	count &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;decrement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	count &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is how we use that module in our test file:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; increment&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; decrement &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./my-app&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;increment&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;decrement&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// The assertion fails if `count` isn&#39;t reset between&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// test runs.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decrement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensuring that developers don&#39;t run into this trap can be a good thing. But it comes at a cost: speed. Creating and tearing down environments for each single test takes a lot of time. So much time in fact, that the majority of time in today&#39;s test suites is spent on exactly this. Running the tests itself barely shows up when you trace your test runner. It pales in comparison to the overhead of test isolation.&lt;/p&gt;
&lt;p&gt;Thing is, most tests don&#39;t need that level of isolation. If we have a function that entirely operates on its inputs and doesn&#39;t mutate shared state, then any form of isolation is unnecessary. Yet, we run the whole environment creation and tear down cost regardless, because the test runner applies these by default. Let&#39;s illustrate that with an example:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Example code that doesn&#39;t rely on shared state at all.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// The function only relies on its inputs.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple &lt;code&gt;add()&lt;/code&gt; function doesn&#39;t need isolation at all. It can be safely called as often as you want, regardless if it&#39;s called in a synchronous context or an asynchronous one.&lt;/p&gt;
&lt;p&gt;When can visualize the cost of isolating across test runners, by running a simple addition test and measuring their time.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;should add numbers&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// We intentionally don&#39;t use an assertion library here&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// to avoid tainting timings by that.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fail&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&#39;s run those tests:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/test-runners.png&quot; alt=&quot;Test runner A takes 45ms, vs test runner B only needing 1ms&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We can already see some difference, but let&#39;s scale that up to a 100 tests by duplicating the &lt;code&gt;add.test.js&lt;/code&gt; test file a hundred times.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/test-runner-100-files.png&quot; alt=&quot;Running 100 test files takes 1.133s in test runner A, vs 10ms in test runner B&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Checking if the addition of two numbers is correct for a hundred times takes a full &lt;code&gt;1.133s&lt;/code&gt; in test runner A and just &lt;code&gt;10ms&lt;/code&gt; in runner B. That&#39;s about &lt;strong&gt;114x&lt;/strong&gt; slower than test runner B.&lt;/p&gt;
&lt;p&gt;If we think about how many computations modern processors can do per second, even the &lt;code&gt;10ms&lt;/code&gt; result is super slow. But despite that it&#39;s still so much faster than test runner A due to not having to constantly create and tear down test environments.&lt;/p&gt;
&lt;h3&gt;What about the DOM?&lt;/h3&gt;
&lt;p&gt;For Preact though, we &lt;em&gt;have&lt;/em&gt; to write to the DOM which is a form of shared state. Creating and destroying the whole document for every test is immensely expensive, so we want to avoid doing that. Instead we leverage a bit of knowledge about Preact to speed up our tests. Preact, like other frontend frameworks, renders &lt;em&gt;into&lt;/em&gt; a single element. So we only need to supply a unique element per test case.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;my test suite&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Runs before every single test case&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;beforeEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Runs after every single test case, regardless if it&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// failed or succeeded.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;afterEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;should render&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;hello world&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;p&gt;hello world&amp;lt;/p&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;//...more tests&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In doing so we set the amount of isolation we need ourselves and avoid spending time unnecessarily setting up isolation containers.&lt;/p&gt;
&lt;h2&gt;Global state&lt;/h2&gt;
&lt;p&gt;Some aspects of Preact, like hooks, require a global variable to track the component that&#39;s currently being rendered. This is not something unique to Preact&#39;s implementation, but rather how the hook API is designed. We could add a way to reset that state similar to how we do with the DOM, but that would introduce a code-path into Preact that&#39;s solely for testing purposes. That&#39;s something we don&#39;t want to do, as we&#39;d rather test Preact in a way that is as close as possible to how it&#39;s used by real users.&lt;/p&gt;
&lt;p&gt;For the longest time in Preact&#39;s history we just dealt with failures caused by global variables not being reset. And to be honest, it was totally fine. It rarely happened and when it did, it pointed to a bug in Preact itself that we would&#39;ve missed otherwise. Because whenever something fails in Preact when users use it, it should not end up in an invalid state anyway. So essentially by us not adding any form of isolation for global state, we got free testing out of that.&lt;/p&gt;
&lt;p&gt;To make debugging a little easier, we added a way to scope globals to a single test file. That way the terminal isn&#39;t spammed with hundreds of failing test cases, but rather to only a few. We do this by creating a separate bundle for every single test file. Everyone of them gets their own copy of Preact and everything else.&lt;/p&gt;
&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;/tests&lt;br /&gt;  render.test.jsx // &amp;lt;- has their own copy of Preact&lt;br /&gt;	component.test.jsx // &amp;lt;- has their own copy of Preact too&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works because our global variables aren&#39;t truly global in the sense that they &lt;em&gt;have&lt;/em&gt; to dangle off of the &lt;code&gt;window&lt;/code&gt;-object. Instead they&#39;re more akin to module scoped variables. So a new instance of the module, creates a new instance of the variable. Creating a unique bundle per test file takes care of that.&lt;/p&gt;
&lt;h2&gt;Preact&#39;s test setup&lt;/h2&gt;
&lt;p&gt;We achieve this through the amazing bundling speed by &lt;a href=&quot;https://esbuild.github.io/&quot;&gt;esbuild&lt;/a&gt; and then cache every bundle that isn&#39;t invalidated by file changes during the development process. So whenever you edit a file and hit save, we&#39;ll re-generate only the bundles that included that file, ping &lt;code&gt;karma&lt;/code&gt; (coordinates the browser) when we&#39;re done and &lt;code&gt;karma&lt;/code&gt; just reloads the browser.&lt;/p&gt;
&lt;p&gt;All this wouldn&#39;t be possible without the compilation speed of &lt;code&gt;esbuild&lt;/code&gt;. It&#39;s basically so fast that it barely makes a difference, if you create one or more bundles. So we happily traded off the slight hit in test run time in favor of a little nicer debugging experience.&lt;/p&gt;
&lt;p&gt;Our whole testing stack basically consists of &lt;code&gt;karma&lt;/code&gt; as the browser coordinator, &lt;code&gt;esbuild&lt;/code&gt; as the bundler and &lt;code&gt;mocha&lt;/code&gt; as the test runner. If there&#39;s one thing I&#39;d love to replace from that stack it would be &lt;code&gt;karma&lt;/code&gt;, because the APIs feel a little dated. But then again I&#39;d happily deal with its oddities, when it allows me to run a 1000 tests in 1s.&lt;/p&gt;
&lt;h2&gt;Let&#39;s make test suites fast&lt;/h2&gt;
&lt;p&gt;In summary, the problem with applying the maximum possible isolation is that everything becomes painfully slow. Knowing which level of isolation you need is a sure way to speed up any test suite. The defaults the currently popular test runners cover have them all turned on to accommodate the widest range of possible use cases. That&#39;s why they are so slow.&lt;/p&gt;
&lt;p&gt;Them being so popular also introduces more of a cultural problem. By now we have a generation of developers who have grown up with those tools being the default ones to pick. They don&#39;t know that their tests could run faster, because they&#39;ve never experienced it. As Andrew puts it on twitter:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/media/tweet-reaction.png&quot; alt=&quot;twitter user @andrewvijay tweets: &amp;quot;What&#39;s making me go mad is that it&#39;s just mocha and karma and nothing fancy&amp;quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And they&#39;re right. There is nothing novel about our approach or how we set up our test suite. Both &lt;code&gt;karma&lt;/code&gt; and &lt;code&gt;mocha&lt;/code&gt; have been around for what feels like forever in web development. They are not new. Only &lt;code&gt;esbuild&lt;/code&gt; is somewhat newer. But then again the usage of &lt;code&gt;esbuild&lt;/code&gt; isn&#39;t unique to our stack either.&lt;/p&gt;
&lt;p&gt;What&#39;s unique to the Preact team though, is that we always go with what the best option is for ourselves and we fill any blind spots in gluing or creating tooling to achieve that. This requires us to be aware of which amount of isolation we need and want from our test suite, and then only applying that. Nothing more, nothing less.&lt;/p&gt;
&lt;p&gt;Because one thing is for sure: Working on Preact is way more fun if all tests complete nearly instantaneous, in 1 second.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>How to skew benchmarks in your favour</title>
    <link href="https://marvinh.dev/blog/benchmarks/"/>
    <updated>2022-08-01T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/benchmarks/</id>
    <content type="html">&lt;p&gt;A common rite of passage for every frontend framework is to compare the execution speed with the established players. After all you want to know if your efforts paid off. Where does your framework positions itself on the performance scale? Maybe you want to make this the main selling point of your framework, so benchmarks can be a motivating factor in squeezing just that little bit more speed out of your code. Whatever your motivation is, let me tell you: Benchmarking is hard and has lots of gotchas. A lot things that can introduce bias to a particular solution.&lt;/p&gt;
&lt;p&gt;I thought I&#39;d be fun to share all the things everyone of us did wrong with getting credible benchmark results at some point in our career. Benchmarking can be addictive and those numbers pretty deceiving if your not careful. I know, because I&#39;ve been there. I made the same mistakes as outlined in this post.&lt;/p&gt;
&lt;p&gt;Let&#39;s dive into how accidental bias is introduced in framework benchmarks.&lt;/p&gt;
&lt;h2&gt;Outdated versions&lt;/h2&gt;
&lt;p&gt;You&#39;ve been working on your framework for quite a while now. You had your benchmark suite since the beginning and have been solely focussed on making your framework the fastest out there. But because you&#39;ve been at it for so long, the other frameworks released new versions which improve their performance. Unbeknownst to you, you&#39;ve accidentally introduced a bias in your suite that favours your own solution.&lt;/p&gt;
&lt;h2&gt;Outdated code&lt;/h2&gt;
&lt;p&gt;It&#39;s not just new framework versions though. With time, programming styles change. Chances are that a framework which has been around for a while has multiple ways of creating - let&#39;s say - a component. If you didn&#39;t update your benchmark, then you were comparing your solution to the how folks would&#39;ve written code for the other framework months or even years ago. It&#39;s likely that the older style uses more closures, more method calls or something else, which makes it slower than the most recent way to write code for it.&lt;/p&gt;
&lt;h2&gt;Incorrect results&lt;/h2&gt;
&lt;p&gt;If you&#39;re rolling your own benchmark suite you probably didn&#39;t bother to write code that asserts that each run rendered the correct result. Maybe your still evaluating if your approach is worth it and skipped the whole attributes thing because you&#39;re more interested in swapping rows. But then it hits you, that the benchmark you&#39;ve been working with uses attributes like ‘class&#39; to add css classes on every element. So what you&#39;ve been measuring all this time, has been how quickly your framework renders no attributes versus a framework that does. Oops!&lt;/p&gt;
&lt;p&gt;Same is true if your code throws an exception early during a run. Depending on the benchmark runner that you&#39;re using, it might not show ‘console.log&#39;s or errors. What you&#39;ve been measuring in this scenario is how quickly your code stopped working. I know, I know. That&#39;s a stupid mistake. But no worries, this happens to the best of us!&lt;/p&gt;
&lt;h2&gt;Batching runs&lt;/h2&gt;
&lt;p&gt;At this point your framework has matured, you&#39;ve added all the bells and whistles and now you think to yourself: &amp;quot;Why should my framework render as fast as it can? Isn&#39;t it enough if I just render at 30fps or 60fps at best?&amp;quot; You&#39;ve seen some other player hype this up into space and you don&#39;t want to miss out on what the cool kids are doing. So you add scheduling to your framework and limit it to a certain amount of renders to achieve a specific fps limit.&lt;/p&gt;
&lt;p&gt;What you didn&#39;t think of though, was that you&#39;re now batching benchmark runs together. Picture this: If it takes your framework 14ms to do the work than each run would take that amount of time. There might be a little variance here and there but every number of every run should fall into the same ballpark. The benchmark runner will receive something like: 14ms, 15ms, 13ms, 14ms, 14ms, 13ms, etc.&lt;/p&gt;
&lt;p&gt;But if your merging runs into one on every fourth invocation, your doing way less work. The benchmark runner now gets numbers looking like: 14ms, 1ms, 0ms, 2ms, 14ms, 0ms, etc. Naturally, the runner will think that your framework was faster. However, it&#39;s not. You just fooled yourself. If you&#39;re using a decent runner, it will print a warning when the variance is too high between runs.&lt;/p&gt;
&lt;h2&gt;Accidental overhead&lt;/h2&gt;
&lt;p&gt;If you didn&#39;t go the custom template route for your framework, chances are that that JSX thing was a pretty compelling option. You&#39;ve added your own &lt;code&gt;createElement&lt;/code&gt; function (the thing JSX gets compiled to) to your package and use that in your benchmark. All the other virtual-dom based frameworks provide their own &lt;code&gt;createElement&lt;/code&gt; implementation too, so it&#39;s fair game, right?&lt;/p&gt;
&lt;p&gt;There is one problem though: The babel transform only allows one single JSX transform per file. That&#39;s annoying! You don&#39;t want to duplicate that section of code for other frameworks as your constantly playing around with different amounts of elements, text nodes or other things.&lt;/p&gt;
&lt;p&gt;&amp;quot;No problem!&amp;quot;, you think to yourself and create a little function that converts the return value of your &lt;code&gt;createElement&lt;/code&gt; calls to whatever the other framework needs.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; vnodes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;		&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Hello world!&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;	&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;my framework&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vnodes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;other framework&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vnodes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;renderOther&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You run the benchmark again and the numbers are amazing! Your framework is miles ahead of everyone else! But wait! Is it really? You go back to your benchmark to verify the numbers. They look too good to be true. Lo and behold, you notice that you call the conversion function &lt;em&gt;during&lt;/em&gt; the run for the other framework. So you didn&#39;t actually measure how long the framework took to render, but how much the difference between converting the input data (=here &lt;code&gt;vnodes&lt;/code&gt;) takes versus not doing that…&lt;/p&gt;
&lt;p&gt;Note that this is just one example of adding overhead. There are many more ways to do that for a specific player.&lt;/p&gt;
&lt;h2&gt;CPU bias&lt;/h2&gt;
&lt;p&gt;You&#39;ve been doing this benchmarking thing for a while now and you&#39;re pretty satisfied with the results. Your framework repeatedly yields better numbers than the others. During one of your benchmark sessions you comment out your benchmark case, which happens to be the first. You run the benchmark again, but something is odd. Suddenly the numbers for the other framework has improved dramatically! What the…?&lt;/p&gt;
&lt;p&gt;Congratulations, you&#39;ve fallen victim to CPU throttling. You know, because benchmarks are typically very CPU intensive, CPUs need to do a lot of work and get pretty hot. If it gets too hot, it must lower the frequency to cold down and avoid damage.&lt;/p&gt;
&lt;p&gt;Problem is that with a lower frequency, it isn&#39;t able to do as much work as it did before anymore. For our benchmark this means that our framework ran at full speed, whereas sometime during the other&#39;s framework run, the CPU got too hot and throttled down, leading to worse results for the other framework. This is especially common with laptops which don&#39;t have space for extensive cooling systems due to their thinness.&lt;/p&gt;
&lt;h2&gt;JIT and GC&lt;/h2&gt;
&lt;p&gt;JavaScript being a Just-In-Time compiled language makes benchmarking even harder. You know the engine doesn&#39;t know about what your doing in that one file. It just views the whole thing as one big program. If it detects that your code doesn&#39;t have side effects and the return value isn&#39;t used, it assumes that this piece is dead code and will eliminate that. You essentially, measured how quickly the engine detects that you&#39;re doing nothing.&lt;/p&gt;
&lt;p&gt;Another common issue are GC (=Garbage Collection) pauses. Because the whole thing is just a program from the engines perspective, the engine determines when it is the best time to do the GC. These heuristics are not deterministic and depend on a lot of other factors of your machine. Depending on when the engine decides to do the GC your benchmark results might vary a lot from run to run.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope this post gave you a little bit of insight as to how easy it is to create a benchmark that favours a specific player or introduces bias. There are obviously lots more ways to do that than shown here, so always check how they arrived at those numbers, before trusting the marketing material. If in doubt the benchmark was likely wrong.&lt;/p&gt;
&lt;p&gt;But know that this is often not done out of malice. Benchmarking &lt;em&gt;is&lt;/em&gt; hard, way harder than it seems! Usually it&#39;s just an honest mistake and the developer in question wasn&#39;t aware of all the gotchas surrounding it.&lt;/p&gt;
&lt;p&gt;Always double check that you&#39;re measuring what you think you are measuring!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>A look at web components</title>
    <link href="https://marvinh.dev/blog/web-components-in-2020/"/>
    <updated>2020-09-04T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/web-components-in-2020/</id>
    <content type="html">&lt;p&gt;About a week ago, I spent time looking at web components, specifically how we can ease converting Preact components. One of my friends is &lt;a href=&quot;https://ddprrt-github-io-git-love-preact.ddprrt.now.sh/go-preact/&quot;&gt;playing around&lt;/a&gt; with them at work and it seemed like a fun challenge to take on. Personally I&#39;ve never used web components before that. I just didn&#39;t have the chance to work on a project which required them so far. So my understanding of them was formed through word of mouth and the usual hot takes that are shared on twitter. But despite that I was geniously curious about them.&lt;/p&gt;
&lt;p&gt;To recap: Our &lt;a href=&quot;https://github.com/preactjs/preact-custom-element&quot;&gt;preact-custom-element&lt;/a&gt; adapter is a small bridge that can turn any Preact component into a web component via a simple function call:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; register &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact-custom-element&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Greeting&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Hello, &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Greeting&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;x-greeting&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Why web components?&lt;/h2&gt;
&lt;p&gt;The main benefit of web components is that you can render them from any other framework as well as in plain HTML. This is useful if you&#39;re building a design system and have to support a plethora of other frameworks that need to consume your components. You can literally drop a web component into any framework and have them work out of the box!&lt;/p&gt;
&lt;p&gt;Despite sounding great on paper there are some head scratching design choices though. The deeper I dive into them the weirder the API feels to me. For granted as a user of &lt;a href=&quot;https://github.com/preactjs/preact-custom-element&quot;&gt;preact-custom-element&lt;/a&gt; you&#39;ll not notice anything of that and I guess this post is more about my first impressions after using for a while.&lt;/p&gt;
&lt;h2&gt;Style encapsulation&lt;/h2&gt;
&lt;p&gt;For years, a lot of thought has been spent battling CSS specificity by scoping CSS to certain areas of a page. Typically this involves prefixing CSS selectors with a unique class name and getting rid of the cascade, so that it is more predictable where the CSS will be applied. The solutions range from CSS-Modules, CSS-in-JS to various compiler-based approaches.&lt;/p&gt;
&lt;p&gt;Web components solve that on the platform level. Each custom element can have a so-called shadow DOM, which is a DOM tree that is hidden from the page and only accessible from inside the custom element.&lt;/p&gt;
&lt;p&gt;Weirdly this is also web components biggest weakness and biggest blocker to use them. Libraries typically don&#39;t support shadow DOM natively (can&#39;t blame them), which leaves us in a weird spot. The intended solution seems to point to having a compiler that figures out which component needs what kind of styles and let it insert the sheet at the appropriate place.&lt;/p&gt;
&lt;p&gt;Without a compiler the solution is more... err gross. It essentially comes down to copying style sheets from the host page into the shadow DOM and attaching a &lt;code&gt;MutationObserver&lt;/code&gt; in case they change.&lt;/p&gt;
&lt;h2&gt;Attributes and Properties&lt;/h2&gt;
&lt;p&gt;There is this myth going around web dev circles you can pass only strings to custom elements. This stems from the eternal confusion about the difference between attributes and properties on the web. In a nutshell, attributes describe the HTML notation and properties of the JavaScript API.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- foo is an attribute here, only strings allowed --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;my-custom-element&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;bar&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;my-custom-element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;vs properties&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; el &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;my-custom-element&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;el&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// foo is a property, can be anything&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The latter allows you to pass any JS value around, whether it is an object, a function or a value of any other type. Declaring an attribute on the other hand is bound to the semantics of HTML and only supports strings. HTML being the natural authoring format of web components it&#39;s very understandable how this myth came to be. Even more so when you consider devs having used other templating languages for years which all support complex data types.&lt;/p&gt;
&lt;p&gt;As with anything in our industry any problem can be duct-taped around and workarounds emerged quickly. The most common one seems to be to append a special suffix (&lt;code&gt;-json&lt;/code&gt;) to each attribute, that serves as a hint to the adapter that the stringified value needs to be parsed.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;my-custom-element&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;foo-json&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;[1, 2, 3]&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;my-custom-element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While it solves one of the shortcomings of the authoring experience in HTML, I&#39;m a bit on the fence about it admittedly. To be honest though, I don&#39;t know of another good solution to this problem, so I can see us adding this in the near future to our adapter via a &lt;code&gt;JSON.parse&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;observedAttributes&lt;/h2&gt;
&lt;p&gt;To support updating components when consumers call &lt;code&gt;el.setAttribute(&#39;foo&#39;, 123)&lt;/code&gt; instead of doing &lt;code&gt;el.foo = 123&lt;/code&gt; the spec expects us to declare which attributes should trigger the update. It&#39;s a static property on the custom element class that is an array of strings.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MyCustomElement&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; observedAttributes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without this our custom element will not update if we call &lt;code&gt;el.setAttribute(&amp;quot;foo&amp;quot;, 123)&lt;/code&gt;. From an end user&#39;s perspective it&#39;s an error prone API to have. It requires the user to keep it in sync with the component implementation itself. We currently require this in our adapter too, but I really want to get rid of it to avoid those pitfalls.&lt;/p&gt;
&lt;p&gt;So I was curious as to how this API came to be. The only answers I could find lie &lt;a href=&quot;https://github.com/w3c/webcomponents/issues/565&quot;&gt;this GitHub issue&lt;/a&gt;. It seems like the main reason was to avoid traveling the C++ to JS bridge all the time. The &lt;code&gt;style&lt;/code&gt; attribute in particular is cited as an example of a very costly update. Scouring various native web component libraries on GitHub it seems like nobody does this though.&lt;/p&gt;
&lt;p&gt;As expected, devs quickly found workarounds for this and it&#39;s mentioned in the same issue. To avoid having to declare the list of observable attributes by hand, we can leverage a &lt;code&gt;MutationObserver&lt;/code&gt; that listens for all attribute changes and we can fire our own update function ourselves.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;observeAttrChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;el&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; observer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MutationObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;mutations&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		mutations&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;mutation&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mutation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;attributes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; newVal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mutation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mutation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attributeName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mutation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attributeName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newVal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	observer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;el&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; observer&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It may not be what the spec authors originally had in mind, but solves a real problem, that can otherwise only be solved with a compiler. A compiler would know the properties of a component and can declare the &lt;code&gt;observedAttributes&lt;/code&gt; property up-front. As an escape hatch we&#39;ll probably only use the &lt;code&gt;MutationObserver&lt;/code&gt; approach, if the user didn&#39;t set &lt;code&gt;observedAttributes&lt;/code&gt; by themselves. Time will tell!&lt;/p&gt;
&lt;h2&gt;Slots are great!&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;slots&amp;gt;&lt;/code&gt;-nodes are in spirit similar to what you&#39;d do with &#39;props.children` in a Preact component. They allow you to place nodes into a specific point in your shadow DOM.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;my-custom-element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;slot&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;some content&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;my-custom-element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- shadow DOM --&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;slot&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- nodes will be placed here --&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;slot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a very neat API and I&#39;m impressed how neatly it integrates with HTML. I think the authors did a fantastic job here!&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This pretty much sums up my journey into web components so far. It&#39;s an interesting aspect of the DOM, albeit being a strange API. Nonetheless, with IE11 finally having an EOL date, it will be interesting to see how it will evolve.&lt;/p&gt;
&lt;p&gt;Despite &lt;a href=&quot;https://dev.to/richharris/why-i-don-t-use-web-components-2cia&quot;&gt;all&lt;/a&gt; &lt;a href=&quot;https://twitter.com/sarahmei/status/1198069119897047041&quot;&gt;the&lt;/a&gt; controversies, the &lt;a href=&quot;https://www.reddit.com/r/firefox/comments/91hbkw/youtube_page_load_is_5x_slower_in_firefox_and/&quot;&gt;troubled history&lt;/a&gt;, and the rough API, I feel like it&#39;s a win for the web. And as usual with Preact we try to support all kinds of use cases as best as we can.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Portals considered harmful</title>
    <link href="https://marvinh.dev/blog/portals-considered-harmful/"/>
    <updated>2020-07-18T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/portals-considered-harmful/</id>
    <content type="html">&lt;p&gt;So today I spent a few hours going through various GitHub repos to see how the &lt;code&gt;Portal&lt;/code&gt; component in various virtual-dom Frameworks is used (and abused?) in the wild. It&#39;s always a fun thing to do for anyone working on frameworks, because users usually discover new ways of how to use features in ways it wasn&#39;t intended by the authors. Sometimes something cool comes out of and sometimes so good that the framework will cater to that use case. It&#39;s not all just sunshine and roses though because sometimes using features in unintended scenarios may break stuff. This is the story of the &lt;code&gt;Portal&lt;/code&gt; component.&lt;/p&gt;
&lt;p&gt;To recap: A &lt;code&gt;Portal&lt;/code&gt; allows you to jump from the current DOM node to a new container and continue rendering from there. A common use case for that are Modals that we may want to render just before the closing &lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt;-tag. Another use case are tooltip components that need to be positioned freely.&lt;/p&gt;
&lt;p&gt;Let&#39;s imagine we have this HTML:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- We&#39;ll render our app here --&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Modals should be rendered here --&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;modals&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and this accompanying Preact code:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; createPortal &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact/compat&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; modalContainer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;modals&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			Hello&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createPortal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;World!&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; modalContainer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;		&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;app&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then the final HTML would look like this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hello&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;modals&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;World!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They look nice, feel good to use, but they aren&#39;t necessary in most cases.&lt;/p&gt;
&lt;h2&gt;CSS to the rescue&lt;/h2&gt;
&lt;p&gt;The modal scenario can be solved without any JavaScript overhead at all. There isn&#39;t always a need to take a sledgehammer to crack a nut. Some things can be done with just a tiny bit of CSS:&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.my-modal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fixed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;z-index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With &lt;code&gt;positon: fixed&lt;/code&gt; we&#39;re creating a new stacking context here and can leverage an additional &lt;code&gt;z-index&lt;/code&gt; property to position the modal above the current visible DOM. This works extremely well! A similar thing can be done for tooltips when they are anchored into the target element&#39;s container.&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;/* Create new stacking context */&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.tooltip&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;/* position tooltip absolute inside container */&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-50%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But with most things web-related there are exceptions to this and the most common one that&#39;s referenced is that you have a parent with &lt;code&gt;overflow: hidden&lt;/code&gt; and you need to break out of that container. Fair bit of warning: Before you do any of that, think twice if you really need &lt;code&gt;Portals&lt;/code&gt; or if you can make you&#39;re life easier with having a proper CSS hierarchy.&lt;/p&gt;
&lt;p&gt;So in those rare cases where Portals are a requirement they do wonders! It&#39;s a good tool to have in ones toolbelt, albeit it should be used wisely and with caution.&lt;/p&gt;
&lt;h2&gt;Why Portals are dangerous&lt;/h2&gt;
&lt;p&gt;Problems arise when they&#39;re used in ways they weren&#39;t intended to be used for. Take the following snippet as an example of that:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; root &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;root&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			foo&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createPortal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createPortal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;		&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we have a Preact application that renders into &lt;code&gt;&amp;lt;div id=&amp;quot;root&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;. So far so good, but now it gets weird: We try to render the word &lt;code&gt;&amp;quot;bar&amp;quot;&lt;/code&gt; into the same container as &lt;code&gt;App&lt;/code&gt; and while we&#39;re at it, let&#39;s take the biscuit by rendering into the same container again, via a second Portal! In the end we have have three virtual-dom trees battleing for the same DOM container. And it gets worse: The expected outcome of that is not &lt;code&gt;&amp;lt;div&amp;gt;foo bar baz&amp;lt;/div&amp;gt;&lt;/code&gt; (spaces added for readability), but instead it is &lt;code&gt;&amp;lt;div&amp;gt;bar baz foo&amp;lt;/div&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Interestingly, every framework renders this a little different:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Framework A: &lt;code&gt;&amp;lt;div&amp;gt;bar baz foo&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Framework B: &lt;code&gt;&amp;lt;div&amp;gt;bar foo baz&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Framework C: &lt;code&gt;&amp;lt;div&amp;gt;baz&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even though everyone&#39;s result is different, everyone is correct at the same time. We can&#39;t fault any of them. So what&#39;s happening here? If you put yourself into the shoes of a framework what would you do? Should the framework remove existing DOM? Should it render before or after the existing child nodes?&lt;/p&gt;
&lt;h3&gt;Framework A&lt;/h3&gt;
&lt;p&gt;Framework A renders Portals before the &lt;code&gt;App&lt;/code&gt; component is appened to the DOM. It&#39;s basically bottom-to-top rendering. We can deduce from the insertion order that this framework has a special branch for dealing with Portals inside an existing tree. Otherwise we&#39;d observe a behavior similar to Framework B where Portals are treated the same way as roots created by &lt;code&gt;render()&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 1st Portal&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  bar&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 2nd Portal&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  bar&lt;br /&gt;  baz&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// App root&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  bar&lt;br /&gt;  baz&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Framework B&lt;/h3&gt;
&lt;p&gt;Framework B tries work around any nodes that are already present in the tree. First the &lt;code&gt;App&lt;/code&gt; will be rendered and both Portals take their turn only after &lt;code&gt;App&lt;/code&gt; is finished. It looks like there is no special branching for &lt;code&gt;Portals&lt;/code&gt; and that it shares the underlying semantics with &lt;code&gt;render()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This means that the reconciler is aware of any existing nodes and when it inserts the Portals it tries its best to move the nodes around the original content. Whilst this logic is not ideal for Portals, it is advantageous for scenarios where Browser extensions like Google Translate which may insert random DOM nodes into our tree.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// App&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 1st Portal&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  bar&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 2nd Portal&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  bar&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  baz&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Framework C&lt;/h3&gt;
&lt;p&gt;Our last, but arguable the most elegant contender Framework C makes short work of the situation. It simply renders over the existing DOM, thereby removing any child nodes that were present at that time. It&#39;s simple, the code is much more elegant and the outcome is predictable. There is no additonal ordering/inserting logic needed. It&#39;s kinda the ultimate zen for developers working on frameworks.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// App&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 1st Portal&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  bar&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 2nd Portal&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  baz&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Err... what about updates?&lt;/h2&gt;
&lt;p&gt;If you think the above scenario was already tough (it is) and you might be wondering what else is out there, then let me take you right to the endboss of Portals: Rendering literally &lt;em&gt;into&lt;/em&gt; each others tree.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; update&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ref &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;ref&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			foo&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createPortal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createPortal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;baz&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; root &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ref&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;i &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;		&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point we&#39;re even deeper in undefined behavior territory. Nobody truly knows what the expected result should be here all frameworks lead to inconsistent results. We can observe the first render and then the second one which changes the order. But starting from there we will never be able to have the same result that we had with our first render. Every render after the first will stay the same. Pretty weird, but to be expected for undefined behavior.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 1st render&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;    bar&lt;br /&gt;    baz&lt;br /&gt;    foo&lt;br /&gt;    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 2nd render&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;    bar&lt;br /&gt;    baz&lt;br /&gt;    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;    foo&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 3rd render&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;root&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;    bar&lt;br /&gt;    baz&lt;br /&gt;    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;click&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;    foo&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 4th render is the same as 3rd&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ultimatively, this is perfectly fine! This was never an intended use case for Portals. At this point all bets are off and the code depends on multiple features being &lt;em&gt;just&lt;/em&gt; right for this scenario. The code depends on the Portal component (obviously), on root detection, stable DOM ordering, unrelated child DOM node detection, etc. The list goes on. On top of that any change in the reconciler has the potential to break that code. It&#39;s brittle and we should make our code more resilient.&lt;/p&gt;
&lt;h2&gt;How to use Portals safely&lt;/h2&gt;
&lt;p&gt;We&#39;ve spoken in-depth about undefined behavior surrounding Portals, but what&#39;s the right and intended way to use them? It all comes down to ensuring that each root has it&#39;s own DOM node. A root created by &lt;code&gt;render()&lt;/code&gt; shouldn&#39;t have to share it&#39;s container with other Portals and Portals shouldn&#39;t have to share them either. And those nodes should be created outside of the framework to ensure that it isn&#39;t suddenly removed whilst the Portal tries to re-render into it. That would be like pulling the rug out from under ones feet.&lt;/p&gt;
&lt;p&gt;Which brings us to the snippet from the beginning which is coincedentally the one that&#39;s usually featured in the documentation of those frameworks.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// HTML:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// &amp;lt;body&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//   &amp;lt;div id=&quot;app&quot; /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//   &amp;lt;div id=&quot;modals&gt; /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// &amp;lt;/body&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; modals &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;modals&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; root &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;app&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			Hello&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createPortal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;World!&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; modals&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;		&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every Portal, every &lt;code&gt;render()&lt;/code&gt; has it&#39;s own DOM node. Nobody renders into each other and no roots have to be shared. It&#39;s a good and peacful world. A world that brings us framework developers joy again!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Stencil Store Challenge</title>
    <link href="https://marvinh.dev/blog/stencil-store-challenge/"/>
    <updated>2020-04-05T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/stencil-store-challenge/</id>
    <content type="html">&lt;p&gt;When Manu from the Stencil team &lt;a href=&quot;https://twitter.com/manucorporat/status/1246194928989937667&quot;&gt;pinged me on Twitter&lt;/a&gt; regarding a code golfing challenge, I just couldn&#39;t resist! So I thought this could be a good exercise to apply techniques we use while working on Preact to a codebase that&#39;s foreign to me.&lt;/p&gt;
&lt;h2&gt;Getting ready&lt;/h2&gt;
&lt;p&gt;After the usual &lt;code&gt;git clone&lt;/code&gt; and &lt;code&gt;npm install&lt;/code&gt; we&#39;re good to go. Before we dive in immediately, we should get ourself familar with the overall goal the code is trying to clear. From the looks of it we&#39;re dealing with an extension to the Stencil framework which attempts to make state management easier. For that they rely on smaller stores where each property can be observed. This library holds the cold for that.s&lt;/p&gt;
&lt;p&gt;Overall I&#39;m getting a Svelte vibe here as they have a similar concept in their frameworks. I expect more frameworks follow in the coming months, but only time will tell for sure. The codebase is authored in TypeScript which is a godsend whenever you have to get familiar with foreign code!&lt;/p&gt;
&lt;h2&gt;Readability&lt;/h2&gt;
&lt;p&gt;The first things I usually do is to remove anything that&#39;s just noise. In particular with TypeScript, developers tend to write overly verbose type declarations, even though TypeScript can infer most of the types automatically. One of such instances can be seen in the implementation for a &lt;code&gt;Map&lt;/code&gt;-like data structure, that&#39;s observable:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; use &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;subscriptions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StoreSubscriptionObject&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;&lt;br /&gt;  subscriptions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StoreSubscriptionObject&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Luckily for us TypeScript is smart enough to unwrap boxed types like arrays, whenever we iterate over it. We can just drop the verbose type name here. The return type is also not needed as the whole function doesn&#39;t make use of the &lt;code&gt;return&lt;/code&gt; keyword. Let&#39;s drop that too! Since the whole code in this repo is pretty small we don&#39;t have to go all enterprise with our naming convention and can shorten &lt;code&gt;StoreSubscriptionObject&lt;/code&gt; to just &lt;code&gt;Subscription&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;subscriptions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Subscription&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;&lt;br /&gt;  subscriptions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Much better! Readabilty is the very foundation to write shorter code. It sets the stage in writing style in which any new code usualy adheres to. This process rarely happens concious, but rather in our unconciesness, because for us humans it&#39;s natural to want to fit into the group. If the writing style favors verbosity it makes it harder (at least for me) to quickly scan code and immediately know what it does. Information densitiy is key here! But don&#39;t overdo it!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: There are many whacky eslint presets for TypeScript out there that force you to manually declare every single type. This is pretty bonkers and completely ignores a huge strength of a mature type system.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Let the golf begin!&lt;/h2&gt;
&lt;p&gt;Looking at the whole function body, we notice a list of &lt;code&gt;if&lt;/code&gt;-statements, that just beg to be optimized.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;subscriptions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Subscription&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;&lt;br /&gt;	subscriptions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;set&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;set&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;set&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;get&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;reset&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; subscription&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we check the type declaration of &lt;code&gt;Subscription&lt;/code&gt; (formely &lt;code&gt;StoreSubscriptionObject&lt;/code&gt;), we can see that this function checks for all possible properties and adds a listener to them. Armed with that knowledge we can rewrite the code to iterate over all properties and assign a listener automatically:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;subscriptions&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Subscription&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;&lt;br /&gt;	subscriptions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;subscription&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; subscription&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although the rewritten function is much shorter I&#39;m not really satisfied with it, as it introduces an iteration in a section which is potentially executed quite a lot. Maybe we can change the way subscriptions are handled itself. The current way relies on each event calling a separate function: One for &lt;code&gt;&amp;quot;set&amp;quot;&lt;/code&gt;, one for &lt;code&gt;&amp;quot;get&amp;quot;&lt;/code&gt; and another one for &lt;code&gt;&amp;quot;reset&amp;quot;&lt;/code&gt;. When it comes to code-golfing function declarations tend to be on the expensive side, same as strings. So maybe, if we manage to it right, we can kill two birds with one stone.&lt;/p&gt;
&lt;p&gt;We&#39;re dealing only with a single Subscription type here which is closely tied to a &lt;code&gt;Map&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Subscription&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;StoreType&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	get&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;KeyFromStoreType &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;keyof&lt;/span&gt; StoreType&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; KeyFromStoreType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	set&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;KeyFromStoreType &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;keyof&lt;/span&gt; StoreType&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;		key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; KeyFromStoreType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		newValue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StoreType&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;KeyFromStoreType&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		oldValue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StoreType&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;KeyFromStoreType&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	reset&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though all properties are declared as optional, they&#39;re always used together in the code that&#39;s provided. There is not a single place where only one of those is used. So how are subscriptions defined? Looking at the internals of &lt;code&gt;ObservableMap&lt;/code&gt; we see 3 separate arrays for each event. This is a good thing as a shared object structure would likely lead to more allocations.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; setListeners&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SetEventHandler&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; getListeners&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GetEventHandler&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; resetListeners&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ResetEventHandler&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// The `on()` function is used to create subscriptions&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; on&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OnHandler&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;eventName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; listeners&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;eventName &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;set&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		listeners &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; setListeners&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;eventName &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;get&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		listeners &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; getListeners&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;eventName &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;reset&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		listeners &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; resetListeners&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Build&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isDev&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Unknown event &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; eventName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	listeners&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;callback&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To set up subscriptions the &lt;code&gt;on(event, callback)&lt;/code&gt; function is used. Looking at the function body we find another long if block that doesn&#39;t bring us joy. The number of possible events is finite and limited to 3, so we could leverage a similar trick like the one we applied earlier to get rid of the &lt;code&gt;if&lt;/code&gt;-statement. We can even remove the error message as the browser&#39;s native one is pretty similar when dealing with missing object keys.&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; handlers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Handlers&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	get&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	set&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	reset&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; on&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OnHandler&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;eventName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	handlers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;eventName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;callback&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This brings us much more joy! So far we&#39;ve only dealt with more &amp;quot;cosmetic&amp;quot; code changes. To be able to really unify the subscription process we have to be a bit more agressive and make changes to the user facing API. This requires more knowledge about how this library is actually used, but I&#39;m not familiar enough with Stencil to make that call. Without that optimzation usually tends to break features.&lt;/p&gt;
&lt;h2&gt;That&#39;s it for now&lt;/h2&gt;
&lt;p&gt;There are probably more areas we can improve on, but I think I&#39;ve spent enough time here. I did conciously not go into optimizations which would save a byte here and there, because the library seems to be newer and in heavy development. ASome potential avenues that immediately come to mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Inline every function that is only used once. This needs to be backed up with numbers, but in general fewer functions compress better with terser+gzip. This is not always the case though, so one should really measure the impact.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Refactor &lt;code&gt;on(event, callback)&lt;/code&gt; to &lt;code&gt;on(callback)&lt;/code&gt; and pass the event type as the first argument. This obviously breaks the existing API and pushes the check which event we&#39;re dealing with to the consumer. It would also shift the performance characteristics, so be careful here! On the plus side we could essentially delete a lot of subscription handling logic, so it&#39;s definitely a tradeoff to make.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Hope this short adventure helped to portray the thinking process of what it&#39;s like to work on Preact. The main takeaways for me from working on Preact are to really understand what the code in front of you is trying to accomplish and to be aware of the context it is used in. Combine that with a laser sharp focus on readabilty and you have the perfect foundation for writing clear and concise code in your heart.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>State of Preact Devtools #2</title>
    <link href="https://marvinh.dev/blog/state-of-devtools-2/"/>
    <updated>2019-09-29T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/state-of-devtools-2/</id>
    <content type="html">&lt;p&gt;A little bit of time has past since the &lt;a href=&quot;https://marvinh.dev/blog/state-of-devtools/&quot;&gt;announcement&lt;/a&gt;
that we are working on our very own devtools extension. A lot has
happend since then and I&#39;d love to share some news about what&#39;s
happening behind the scenes. The good news: I&#39;d consider the current
code feature-complete and the next will be the polishing phase. It&#39;s
safe to say that things are progressing nicely without any hiccups.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/media/preact-devtools-september.png&quot; alt=&quot;State of preact devtools in September&quot; /&gt;
&lt;em&gt;Screenshot of the current state of the extension.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The main star of the show is obviously the element viewer itself.
Having a way to peek at the component tree is incredibly helpful to
understand how the screen was rendered. On the surface sucha a
tree-view seems easy enough to write down, but when I compared all the
devtools from other frameworks and browsers I noticed lots of UX
differences, which in my opinion can really make (or break) the
usefulness of the devtools.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/media/preact-devtools-collapsed.png&quot; alt=&quot;Collapsed tree in devtools&quot; /&gt;
&lt;img src=&quot;https://marvinh.dev/blog/media/preact-devtools-collapsed2.png&quot; alt=&quot;Uncollapsed tree in devtools&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Pressing the right arrow key here should uncollapse the tree and move the selection to the first child.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Personally, the arrow keys get a lot of usage on my machine. I&#39;m using
them as one of the main ways to navigate around and collapse/hide
component trees. It&#39;s suprising to me that this is sometimes
overlooked or has issues like only moving one position when holding
down an arrow key. Even if the keyboard navigation is present, one
has to make sure that the viewport follows the currently selected
item. Otherwise it would move out of sight, hidden behind some
scrollbars.&lt;/p&gt;
&lt;p&gt;In the end I personally liked the UX of the react-devtools extension
the most. It&#39;s no secret that &lt;a href=&quot;https://twitter.com/brian_d_vaughn&quot;&gt;Brian Vaughn&lt;/a&gt;
and the other members of the React team have done an outstanding job
there. This is something that I always tried to keep in mind when
writing the devtools extension for Preact, because I know a lot of
in my area who frequently switch between Preact and React projects.
Deviating too much there would ailenate their workflow and cause
unnecessary friction for our users. It some minor differences though,
which we&#39;ll go on in detail later.&lt;/p&gt;
&lt;h2&gt;Filtering for a less cluttered view&lt;/h2&gt;
&lt;p&gt;The component tree can get quite long, especially when a css-in-js
library is used which creates an additional component for every styled
DOM node. This often leads to cluttered component trees, making it
hard to see the ones that matter. It&#39;s another feature I frequently
make use of when jumping between projects and I tried to change the
UX a little to better reflect the quick need of changing some filters.&lt;/p&gt;
&lt;p&gt;Instead of opening them in a dedicated modal, they&#39;ll open in a simple
popover without hiding the tree view. The built-in filters to hide
&lt;code&gt;Fragments&lt;/code&gt; or DOM nodes are always present, so that you can easily
toggle them on or off. Note that if you uncheck the &lt;code&gt;Fragment&lt;/code&gt; filter,
you&#39;ll always see a &lt;code&gt;Fragment&lt;/code&gt; at the top of the tree. This is because
we&#39;re re-using &lt;code&gt;Fragment&lt;/code&gt;s as root nodes in Preact.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/media/preact-devtools-filters.png&quot; alt=&quot;The new filter popup&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The new filter popup where you can quickly hide specific components. Don&#39;t mind the visual glitch with the focus ring. This will be fixed.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Clicking on the &lt;code&gt;Add filter&lt;/code&gt; button will add a new one, where you can
enter a partial component name, that should be hidden. It will
directly focus the text input field, which is another little UX detail
that makes a difference in day-to-day usage.&lt;/p&gt;
&lt;h2&gt;Searching for a specific component&lt;/h2&gt;
&lt;p&gt;When you&#39;re implementing filtering into something you&#39;ll probably will
want to add it&#39;s cousin - the search - too! There are always times
where you need to find specific components and see their hierachical
relation in the tree to other components. The search feature does
exactly that and works very similar to the native &lt;code&gt;ctrl + f / cmd + f&lt;/code&gt;
feature found in all browsers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/media/preact-devtools-search.png&quot; alt=&quot;Search in preact-devtools&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But I also made sure pay particular attention to the same little UX
features like with the tree view. In this case just pressing &lt;code&gt;Enter&lt;/code&gt;
will highlight the next search result and pressing &lt;code&gt;Shift + Enter&lt;/code&gt;
will do the opposite. The latter is often forgotten in different
implementations.&lt;/p&gt;
&lt;h2&gt;Full on props modifications encouraged&lt;/h2&gt;
&lt;p&gt;Equally important as the tree view is the sidebar panel where &lt;code&gt;props&lt;/code&gt;,
&lt;code&gt;state&lt;/code&gt;, &lt;code&gt;context&lt;/code&gt; and later &lt;code&gt;hooks&lt;/code&gt; (upcoming feature) can be
inspected and modified at-will. For me this aspect of the web has
always been the most exciting. We can modify pretty much anything we
want and can see the results immedialely. I do think a tight feedback
loop makes a world of difference during development.&lt;/p&gt;
&lt;p&gt;For this reason I was blown away when I came across &lt;a href=&quot;https://github.com/facebook/react/pull/16700&quot;&gt;this PR&lt;/a&gt; in the react repo, that allows
more freedom when editing values. You know the old devtools where
always a bit lacking in that regard, because you could only modify
&amp;quot;primitve&amp;quot; values like &lt;code&gt;booleans&lt;/code&gt;, &lt;code&gt;strings&lt;/code&gt;, &lt;code&gt;numbers&lt;/code&gt;, etc, but
never &lt;code&gt;arrays&lt;/code&gt; or &lt;code&gt;objects&lt;/code&gt;. On top of that there wasn&#39;t any way to
interactively add new props like you can when adding CSS properties
in the native HTML elements panel.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/media/preact-devtools-props-editor.png&quot; alt=&quot;Live editing of props inside the devtools&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Changes are immediately reflected in your app and you can even undo
them in case you they turned out to be wrong.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In short I fell in love and it became a thing I absolutely wanted to
enjoy in the devtools for Preact. Two days later the code got
committed and thanks to the excellent &lt;a href=&quot;https://github.com/json5/json5&quot;&gt;json5&lt;/a&gt;
library the input for complex objects was pretty easy to do. It
publish an fork which supports the ES modules syntax (&lt;code&gt;json5-es&lt;/code&gt;)
until the PR is merged in case you&#39;re interested.&lt;/p&gt;
&lt;h2&gt;Component Holmes Inspector&lt;/h2&gt;
&lt;p&gt;In the scenario that you know what component rendered a specific
element on the page, you can use the neat inspect button which allows
you to just click on the element on the page and it will automatically
highlight the correct node in the tree view.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/media/preact-devtools-meta.png&quot; alt=&quot;Inspecting the devtools with devtools&quot; /&gt;
&lt;em&gt;Inspecting the devtools with devtools... That&#39;s meta!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This again may seem like a simple feature, but quickly gets more
complex once you add fitlering to the mix. Because what happens when
the inspected element is filtered away? The solution is to just walk
upwards until you find a non-filtered parent. It&#39;s doable, no doubt
and no complains here, but it&#39;s another thing to be mindful of when
adding such a feature. The same is true for collapsed subtrees. There
you need to uncollapse them as you go.&lt;/p&gt;
&lt;h2&gt;What&#39;s next&lt;/h2&gt;
&lt;p&gt;Now that I feel confident to have a solid feature-set for an inital
release, I&#39;ll spent the next available time slots on polishing all
the things. There are still some obvious bugs here and there which
need to be shaken out. The polishing phase is not to be underestimated
from a planning perspective, but there aren&#39;t any blockers there. It
just takes a bit of time.&lt;/p&gt;
&lt;p&gt;After that there are many features that I have set my sights on. One
of them is &lt;code&gt;hooks&lt;/code&gt; state which I have skipped so far, because it
needs some modifications to &lt;code&gt;preact/hooks&lt;/code&gt; before this can work. I
still feel like I haven&#39;t found the right way™️ to go about it yet.
I&#39;m also very interested to get some sort of highlight-updates added
as it was feature I really loved and used a ton in the past. We&#39;ll
see, time will tell ;)&lt;/p&gt;
&lt;p&gt;I&#39;m not sure how much time I&#39;ll be able to dedicate to that during
October, as I&#39;ll be touring around on various conferences and Meetups.
One is the frehsly baked &lt;a href=&quot;https://localhost.engineering/&quot;&gt;localhost.engineering&lt;/a&gt; conference in Düsseldorf, Germany and the
other one is &lt;a href=&quot;https://scriptconf.org/&quot;&gt;scriptconf&lt;/a&gt; which will be held in Linz, Austria. If you share, be sure to hit me up for chat. And I&#39;ll bring a good bunch of stickers with me in case you fancy those ;)&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>State of Preact Devtools</title>
    <link href="https://marvinh.dev/blog/state-of-devtools/"/>
    <updated>2019-09-04T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/state-of-devtools/</id>
    <content type="html">&lt;p&gt;As many of you know the react team released a revamped &lt;a href=&quot;https://reactjs.org/blog/2019/08/15/new-react-devtools.html&quot;&gt;devtools extension&lt;/a&gt; recently. The improvements are countless and not just for endusers, but also the protocol has changed substaintially for the better. Whereas the previous iteration always asked for the information of the complete tree and every detail at once, the new version follows more a pull based approach. It only requests the data it needs and only when it&#39;s needed. That&#39;s a stark contrast!&lt;/p&gt;
&lt;p&gt;Another neat change is that the mappings for elements now happens on the consumer side. When you&#39;re attaching devtools to a virtual-DOM-based framework you need to notify the extension of every change in the tree or duplicate some of the reconciliation when element order changes. Both approaches are valid, but whatever you choose, you need to have a way to match old and new elements. You need to know that the just rendered element matches exactly a node on the other side.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/media/preact-devtools-old.png&quot; alt=&quot;Old react-devtools&quot; /&gt;
&lt;em&gt;The previous version of &lt;code&gt;react-devtools&lt;/code&gt; was great for its time, but known to be a little slow.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;With the previous adapter we had to pretend to actually &lt;em&gt;be&lt;/em&gt; React, which isn&#39;t always easy. With the new one we could now just write our own and attach it to the extension. I started on that adventure in April and got my first components to show up in the new extension (note that it was still marked as experimental back then and months away from a release).&lt;/p&gt;
&lt;p&gt;Armed with new possibilities we could do the mapping ourselves in our own preact adapter. Typically you&#39;ll store a &lt;code&gt;Map&lt;/code&gt; somewhere where you assign an &lt;code&gt;ID&lt;/code&gt; for each node. That way you can easily track parent relationships and ordering without having to care about framework specific details. On the plus side this makes the extension code much easier to reason about.&lt;/p&gt;
&lt;p&gt;A few weeks later I was able to finally crack it. I got the whole tree showing up in the extension! One by one I spent the following months ticking of one box after another. One week I managed to get &lt;code&gt;props&lt;/code&gt; to show up, the other week &lt;code&gt;state&lt;/code&gt; and I really made lots of progress.&lt;/p&gt;
&lt;p&gt;Not to say that I didn&#39;t have my moments where I was stuck on something, but in the end it worked out. Our own &lt;a href=&quot;https://github.com/preactjs/preact/tree/master/demo&quot;&gt;demo app&lt;/a&gt; we use to test real world code with, was crucial here in aiding my plans.&lt;/p&gt;
&lt;h2&gt;The difficult bridge to react-devtools&lt;/h2&gt;
&lt;p&gt;Problems started to arise once the new devtools was released and we received a wider audience of users eager to test it out with Preact. Whilst some issues could be fixed, others could not. I spent 4 full days trying to make it work and ultimately had to call in the defeat.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/media/react-devtools-broken.png&quot; alt=&quot;The new react-devtools with Preact&quot; /&gt;
&lt;em&gt;The new &lt;code&gt;react-devtools&lt;/code&gt; extension with Preact. As you can see the props panel is broken.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The reason it&#39;s difficult is that under the hood Preact and React do things quite a bit different from each other. Due to the focus on concurrent rendering, React splits everything into so called commits. It describes all the changes that were made in the current render request (for a lack of a better word). This means that each call to &lt;code&gt;setState/forceUpdate&lt;/code&gt; or the toplevel &lt;code&gt;render()&lt;/code&gt; function will create such a commit.&lt;/p&gt;
&lt;p&gt;Those commits are then queued up into a message the devtools understands. Those events will be automatically in order so you can traverse it linearly on the extension side. That&#39;s pretty great for performance!&lt;/p&gt;
&lt;p&gt;Trouble is that in Preact we don&#39;t have such a strict model of commits (yet?). When you call &lt;code&gt;forceUpdate&lt;/code&gt; on a &lt;code&gt;component&lt;/code&gt; we pause the current render and execute the new one in-between before continuing with the previous one. This obviously breaks the strict ordering rules of events for the devtools.&lt;/p&gt;
&lt;p&gt;Note that this is just one of the issues I was facing, there are many more subtle ones, simply because both frameworks work so differently. That&#39;s even more true for Preact 8.x which is a whole different beast. Overall the main thing that broke me is the insane amount of time I spent on the adapter that masks Preact as React. I&#39;ve come to the conclusion that it&#39;s better for us in the longterm when we have our own extension.&lt;/p&gt;
&lt;p&gt;I want to be clear here that the &lt;code&gt;react-devtools&lt;/code&gt; extension (as the name implies) is only targeted at React. We were fully aware that we didn&#39;t get any official support for that and for good reason (obviously). The devtools are excellent and some of the best we&#39;ve seen. They even added tiny features just for us to make it easier to interface with it. We&#39;re super grateful for that and we have a lot to thank them for!&lt;/p&gt;
&lt;h2&gt;It&#39;s not you, it&#39;s me&lt;/h2&gt;
&lt;p&gt;I&#39;m fully aware that the current situation where the devtools are kinda broken sucks. And I hate that. It makes me sad because we nearly got it working and I personally see any form of devtools as a huge asset for any framework.&lt;/p&gt;
&lt;p&gt;Faced with a hard decision, I went with giving our own extension a go. I&#39;ve spent the past 2.5 weeks working on that and a prototype is finally coming together. For now my main focus is on getting the elements panel right. Additional features such as some sort of profiling tool will come later.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/media/preact-devtools.png&quot; alt=&quot;Preact Devtools&quot; /&gt;
&lt;em&gt;Don&#39;t mind the minor visual glitches (heavy work in progress). I&#39;m more focused on getting the features stable and robust first. It supports both light and dark mode if you&#39;re curious.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Introducing Preact Devtools&lt;/h2&gt;
&lt;p&gt;In just a little more than 2 weeks the basic features are already there. The extension will display a colored icon when it detects Preact on the current page. It will display the current mounted component tree (update and unmount still needs work). It can inspect &lt;code&gt;props&lt;/code&gt; and change them. It can highlight the current node in the viewport and most importantely: It&#39;s a lot easier for us to work with.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/media/preact-devtools-light.png&quot; alt=&quot;Preact Devtools in light design&quot; /&gt;
&lt;em&gt;Light version of the Devtools (heavy work in progress).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;But it will also take time, more than I&#39;d like it too. Yet I feel like this is the right direction to take. Although I know the &lt;code&gt;react-devtools&lt;/code&gt; code like the back of my hand, I was able to add more features in the past two weeks than I was able to in the past months.&lt;/p&gt;
&lt;p&gt;Having our own devtools also means that we can explore more Preact related aspects like visualizing other types of data related to components in the tree. This opens a lot of doors for us!&lt;/p&gt;
&lt;p&gt;Right now it&#39;s in a rough state and I&#39;m confident that it will mature in the next weeks. It&#39;s way too early to set on a release (or pre-release) date, but I just wanted to share with you the current state of devtools, so that you all know what&#39;s happening.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Hooks vs Classes a few months later</title>
    <link href="https://marvinh.dev/blog/hooks-vs-classes-a-few-months-later/"/>
    <updated>2019-06-13T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/hooks-vs-classes-a-few-months-later/</id>
    <content type="html">&lt;p&gt;It&#39;s been a while now that the &lt;code&gt;hooks&lt;/code&gt; concept took over the frontend world by
storm. Originally researched by the very talented React team it solves some
longstanding issues surrounding the best way to make behaviours easily composable
and shareable. A lot has been written about what they are and how to use them,
so I won&#39;t repeat that here. Instead I&#39;d love to share the maintainers
perspective on them.&lt;/p&gt;
&lt;p&gt;Once we announced our first Preact X alpha many users immediately jumped on the
hooks bandwaggon, and for good reasons! Since that release we&#39;ve received
significant less bug reports and support questions on slack regarding the use
of components. With &lt;code&gt;hooks&lt;/code&gt; there simply aren&#39;t many possiblities to tread down
the wrong path anymore. It&#39;s a solid API that in my experience is even easier
to understand for newcomers, which is always a plus!&lt;/p&gt;
&lt;p&gt;So what are the footguns users run into with the Class-API you ask? For that I
collected some of the more frequent snippets we received in the last months.&lt;/p&gt;
&lt;h2&gt;Initializing state is confusing&lt;/h2&gt;
&lt;p&gt;This is something that trips up people coming from different languages a lot.
You know in most OOP-based programming languages there is the concept of
setters. It&#39;s arguably less common in the JavaScript world, but one can still
find it from time to time.&lt;/p&gt;
&lt;p&gt;The idea is that you don&#39;t modify property directly and instead opt to set them
using special setter functions. This allows the underlying class to change/rename
the variables under the hood, without the fear of breaking userland code.
Because of that you never set the properties directly and the natural conclusion
would be to write something like this to initialize the state of a component:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// Don&#39;t do this.&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thing is that this is effectively a no-op. It doesn&#39;t initialize the state.
Instead we deviate from the common getter/setter concept and set it directly:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token literal-property property&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is, again, another think we have to consciously keep in mind. It&#39;s
something newcomers seem to run into once in a while. With hooks we can sidestep
that completely, because they&#39;re just a function call. We just pass the initial
state upon calling it:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setFoo&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;//...snip&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Nested &lt;code&gt;setState&lt;/code&gt; calls&lt;/h2&gt;
&lt;p&gt;After pondering about a use case for a while, I still have no idea where one
would want to use nested &lt;code&gt;setState&lt;/code&gt; calls. A while ago we received a bug
report that had code similar to this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Don&#39;t do this&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;bar&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;bob&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second argument of the &lt;code&gt;setState&lt;/code&gt; function is a callback that will be called
when the state is updated. It&#39;s super rare in the real-world, and I personally
think this is in hindsight a mistake. This pattern is bad for performance,
because now you&#39;re rendering the component 3 times instead of once.&lt;/p&gt;
&lt;p&gt;So what is this code trying to do? What makes it so important to differentiate
the second from the third render? If you know a valid use case, please message
me. I&#39;d love to know more!&lt;/p&gt;
&lt;h2&gt;Lifecycles were a bit messy&lt;/h2&gt;
&lt;p&gt;One tricky thing with the old lifecycle API was the way they were called.
Imagine a scenario where you want to execute something whenever your component
is mounted or updates. Like a tooltip where you need to position it yourself
via JS to know if there is enough space to show it above a button. Otherwise
the tooltip should be moved below the button. It&#39;s a super common thing but
back than we had to split it up into &lt;code&gt;componentDidMount&lt;/code&gt; and
&lt;code&gt;componentDidUpdate&lt;/code&gt;. Naturally the pattern that emerged there was that both
&lt;code&gt;cDM&lt;/code&gt; and &lt;code&gt;cDU&lt;/code&gt; lifecycle hooks just called another method.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Tooltip&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;componentDidMount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;measurePosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;componentDidUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;measurePosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;measurePosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// do some measurements&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now compare the above to the hooks version:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Tooltip&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// do measurements here&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But the real trouble happens behind the scenes. To successfully support
class-based Lifecycles we have to be extremely careful with the arguments. We
have to always drag the previous &lt;code&gt;props&lt;/code&gt; and &lt;code&gt;state&lt;/code&gt; around and be super careful
when calling lifecycles that can change them like &lt;code&gt;getDerivedStateFromProps&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The question that pops to mind is: &amp;quot;Why do we even have to do that?&amp;quot; And the
reason is that both &lt;code&gt;props&lt;/code&gt; and &lt;code&gt;state&lt;/code&gt; are reference bound. When we update one
of them we set &lt;code&gt;this.props&lt;/code&gt; or &lt;code&gt;this.state&lt;/code&gt; to the new value and thus losing
the reference to previous one. To prevent that we store it the previous version
in a variable just before updating it.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Pseudo code, we don&#39;t assign `this.state` synchronously&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prevState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;enqueueRender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// later during render&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; inst &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MyComponentInstance&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;inst&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldComponentUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inst&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nextProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; inst&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;nextState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, in hooks-land this becomes a lot easier because we can use plain JS to
take advantage of closures. We don&#39;t need to store the new state immediately
on our component instance and can defer that to when the component has
completed the diffing phase. That&#39;s another case where we can save a few bytes!&lt;/p&gt;
&lt;h2&gt;Less footguns = less maintenance&lt;/h2&gt;
&lt;p&gt;At this point one can argue that we could at more checks in &lt;code&gt;preact/debug&lt;/code&gt; or
create a linter preset to warn about common misuses of the Class-API. Have no
doubt, this would work and help our users a lot. But there is a hidden cost
for us as maintainers in that we have to write code especially for debugging
purposes. On top of that it&#39;s another thing that needs to be documented,
throughouly tested and so on...&lt;/p&gt;
&lt;p&gt;Instead it&#39;s much more desireable for us to have a solid API which doesn&#39;t
allow these issues to pop up in the first place. With something like that we
can save us a lot of work, that we can use to work on nextgen features.&lt;/p&gt;
&lt;p&gt;Are hooks the perfect API? I don&#39;t think so, but it&#39;s one of thos that has
gotten us the closest so far. It definitely frees us maintainers a lot of time
spent answering questions. I&#39;m excited to see where the future will lead us,
even more so that more frameworks are much more willing to experiment with new
APIs than just a few years before.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Applying Preact minification tricks to CSS</title>
    <link href="https://marvinh.dev/blog/applying-preact-minification-tricks-to-css/"/>
    <updated>2019-04-15T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/applying-preact-minification-tricks-to-css/</id>
    <content type="html">&lt;p&gt;One thing we&#39;re known for over at Preact is making code a lot smaller than it
originally was. In fact the tiny size is one of the main strengths of Preact. A few days ago a friend of mine &lt;a href=&quot;https://mobile.twitter.com/djfarly&quot;&gt;Jan Willem Henckel&lt;/a&gt;
shared a link to &lt;a href=&quot;https://cssbattle.dev/&quot;&gt;CSSBattle&lt;/a&gt;, where users can compete
against each other in trying to replicate an image with just CSS and HTML.
The kicker? You have to use as few characters as possible to get the most points!
Currently he holds the top spot with a mere 56 characters for rendering the following page:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/applying-preact-minification-tricks-to-css/media/css-battle.png&quot; alt=&quot;Image Of The First Challenge Of CSSBattle&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It&#39;s a greenish rectangle inside a brown canvas. Not the prettiest for sure, but
enough to get us going. Intrigued and always up for a code challenge, I gave it
a go. Although I mainly write JavaScript or TypeScript these days I was curious
to see how much of the knowledge gained from working on Preact can be applied
to other areas. The mindset for minifying and optimizing code in general should
be very similar in theory, but we&#39;re dealing with a different language. Some of
the optimization tricks from JS obviously won&#39;t transfer to that.&lt;/p&gt;
&lt;p&gt;The challenge starts with the following code snippet for the image above:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;br /&gt;	&lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #dd6b4d&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see it&#39;s just a single &lt;code&gt;div&lt;/code&gt; and a &lt;code&gt;style&lt;/code&gt; element that holds the CSS.
All in all this snippet comes in at 105 characters which is close to twice as
much as our target. Here we notice that we have a few repeating strings. The
word &lt;code&gt;div&lt;/code&gt; is used three times and &lt;code&gt;style&lt;/code&gt; occurs twice. Unfortunately, the
challenge is measured in characters and not in bytes. If would be the latter
we could leverage the famous gzip algorithm to compress the final output like
we do with Preact.&lt;/p&gt;
&lt;h2&gt;Minification tricks that work everywhere&lt;/h2&gt;
&lt;p&gt;The first obvious optimization we can do is to get rid of unnecessary whitespace
and reduce it to one line:&lt;/p&gt;
&lt;!-- prettier-ignore --&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;#dd6b4d&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the real world this is typically handled automatically by build-tools as the
last step in the asset pipeline. That&#39;s a good thing, because the above snippet
is a lot harder to read than the multiline version. With just this we&#39;re down
to 75 characters, but we can do a lot more. Since pretty much the dawn of time
HTML has supported the &lt;code&gt;style&lt;/code&gt;-attribute on elements that can be used to apply
CSS inline. This helps us to remove both the opening and closing &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag
all together. Even better: It also allows us to remove the selector portion of
the CSS 🎉&lt;/p&gt;
&lt;!-- prettier-ignore --&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;100px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;#dd6b4d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This lands us at 63 characters, but if we compare our result it looks nothing
like the image we&#39;re trying to render:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/applying-preact-minification-tricks-to-css/media/css-battle-start.png&quot; alt=&quot;Image of our current status&quot; /&gt;&lt;/p&gt;
&lt;p&gt;We still need to change the colors and reposition the rectangle. So how can we
do that? We don&#39;t want to add a second element as they are quite costly in terms
of characters. That&#39;s when I remembered the &lt;a href=&quot;https://a.singlediv.com/&quot;&gt;single div challenge&lt;/a&gt; from a few years back which challenged web developers and designers around
the world to make the most out of a single &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;. That was when the industry
collectively discovered that we can use (abuse?) the &lt;code&gt;box-shadow&lt;/code&gt;-property to
create multiple shapes at once.&lt;/p&gt;
&lt;p&gt;To do that we need to remove the &lt;code&gt;height&lt;/code&gt; and &lt;code&gt;width&lt;/code&gt; declaration to make our
&lt;code&gt;div&lt;/code&gt; spread over the whole canvas. Remember that a &lt;code&gt;div&lt;/code&gt; is a block-element!
In exchange I tried to position the inner rectangle with a few &lt;code&gt;em&lt;/code&gt; values and
applied the correct color codes.&lt;/p&gt;
&lt;!-- prettier-ignore --&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;-24em 0 0 12em#b5e0ba&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0 0 0 19em#5d3a3a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have gotten a little bigger with this version at 70 characters, but our
result now looks exactly like our target image.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/applying-preact-minification-tricks-to-css/media/css-battle.png&quot; alt=&quot;Our result looks like the target image&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Coloring strategies&lt;/h2&gt;
&lt;p&gt;Looking at Jan&#39;s score we can see that it&#39;s possible to reduce our snippet by
14 more characters. Maybe we can save something by finding more optimal color
codes. As most of you know there are various ways to simplify the hexadecimal
string. CSS allows you to turn &lt;code&gt;#ffaacc&lt;/code&gt; into &lt;code&gt;#fac&lt;/code&gt; allowing us to save 3
characters. Sometimes the color code is even shorter than the hex string as it
is the case with &lt;code&gt;gray&lt;/code&gt; versus &lt;code&gt;#c0c0c0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Comparing the expected color values with the CSS spec unfortunately doesn&#39;t
leave us with any reductions. The color codes are unique and can&#39;t be
compressed. But we&#39;re now defeated yet, there are still various avenues we can
explore 🍀.&lt;/p&gt;
&lt;p&gt;One of those is a feature that was introduced with HTML5, which allows you to
remove the quotes around an attribute. Our value needs whitespace in it, but it
turns out that we can substitute the space character with a plus and save the
two quoting letters.&lt;/p&gt;
&lt;!-- prettier-ignore --&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;-24em+0+0+12em#b5e0ba&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0+0+0+19em#5d3a3a&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point I was wondering whether there are any other length units that are
bigger than &lt;code&gt;em&lt;/code&gt;. Jan gave me a hint about this before starting this challenge
so it should work out. Looking at all the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/length&quot;&gt;available units&lt;/a&gt; in CSS it seems like &lt;code&gt;in&lt;/code&gt; for &lt;code&gt;inches&lt;/code&gt; fits the bill.&lt;/p&gt;
&lt;!-- prettier-ignore --&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;-4in+0+0+2in#b5e0ba&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0+0+0+4in#5d3a3a&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After converting the unit to inches we reached 65 characters which is better but
nowhere close to 56. What else can we do? Remember when we established that we
need a block-level element to span over the whole canvas? Maybe there is another
one that has a shorter name. Reading through both MDN and the HTML-Spec I came
across &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements&quot;&gt;this list&lt;/a&gt; of all elements that are considered to be block-level. We had the right right
nose for it here, because luckily for us the &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;-tag is also part of the list!&lt;/p&gt;
&lt;p&gt;Armed with our new findings we can swap out our &lt;code&gt;div&lt;/code&gt; for a simple &lt;code&gt;p&lt;/code&gt; element.&lt;/p&gt;
&lt;!-- prettier-ignore --&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;-4in+0+0+2in#b5e0ba&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0+0+0+4in#5d3a3a&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Quirksmode is back from the dead&lt;/h2&gt;
&lt;p&gt;Only 5 more characters to go! So let&#39;s do something that you should never do
outside of code golfing challenges: Reduce the code by making it invalid HTML
but let it still parse correctly. Let me repeat that: &lt;strong&gt;Don&#39;t do this in any
project!&lt;/strong&gt; Those of us who&#39;ve been around a bit longer remember the times when
cross-browser compatibility was not so great and to be frank quite lacking.
During those dark times of the web it was quite common for web pages to serve
invalid HTML 🤷&lt;/p&gt;
&lt;p&gt;This is troubling for the browser because now it only has two options on how to
proceed:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Bail out of rendering (bad, white page)&lt;/li&gt;
&lt;li&gt;Try to render it anyway and guess what the developer meant&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first option is a no go, because users never assume that the page itself is
at fault. When something works in one browser and not the other the usual
conclusion is that the browser is broken. And how should they know that the page
is actually causing issues? That burden lies on us developers to make sure we
serve valid HTML.&lt;/p&gt;
&lt;p&gt;To combat this browser have implemented a &amp;quot;quirksmode&amp;quot; in which the browser tries
to guess what the correct result should be. Depending on the browser that is
used this can lead to quite unpredictable results, but in our case, and only
because it&#39;s a code-golfing challenge, we can get away with it as long as the
rendered result stays the same.&lt;/p&gt;
&lt;p&gt;What is this unholyness you ask? Well, we can remove the closing &lt;code&gt;&amp;lt;/p&amp;gt;&lt;/code&gt;-tag 😬:&lt;/p&gt;
&lt;!-- prettier-ignore --&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;-4in+0+0+2in#b5e0ba&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0+0+0+4in#5d3a3a&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&#39;re now at 57 characters which is just one more than Jan&#39;s high score.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://marvinh.dev/blog/applying-preact-minification-tricks-to-css/media/css-battle-leaderboard.png&quot; alt=&quot;Leaderboard for the first Challenge of CSSBattle&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I tried finding a way to remove the last character but after trying for longer
than I should, I haven&#39;t found a way to do so. Nonetheless I&#39;m happy with the
result. We effectively reduced the character count by half which is solid!&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Even though CSS and HTML is quite different from JavaScript we could re-use a
lot of common knowledge for minification. The most obvious one being stripping
all possible whitespace characters. On top of that we leveraged more and more
domain specific strategies by examining the spec closely. Although the result
is not pretty visually we learned a lot of neat tricks in the process that can
be very useful in real world applications 🚀🙌&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>The Double Encoded VNode</title>
    <link href="https://marvinh.dev/blog/the-double-encoded-vnode/"/>
    <updated>2019-03-30T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/the-double-encoded-vnode/</id>
    <content type="html">&lt;p&gt;Someone in our Preact Slack channel (&lt;em&gt;&lt;a href=&quot;https://preact.slack.com/&quot;&gt;join here&lt;/a&gt;&lt;/em&gt;) was running into an issue where the page would be blank as soon as the page loaded. Luckily we got a nice exception logged in the browser console. Error stack traces are awesome as they point you directly to the portion where the error occured and allow you to follow back the trail to see how the error came to be.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;preact.mjs:256 Uncaught DOMException:&lt;br /&gt;&lt;br /&gt;  Failed to execute &lt;span class=&quot;token string&quot;&gt;&#39;createElement&#39;&lt;/span&gt; on &lt;span class=&quot;token string&quot;&gt;&#39;Document&#39;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; The tag name provided&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[object Object]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; is not a valid name.&lt;br /&gt;&lt;br /&gt;    at C &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;webpack-internal:///&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/node_modules/preact/dist/preact.mjs:213:155&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    at x &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;webpack-internal:///&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/node_modules/preact/dist/preact.mjs:175:16&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    at b &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;webpack-internal:///&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/node_modules/preact/dist/preact.mjs:108:83&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    at j &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;webpack-internal:///&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/node_modules/preact/dist/preact.mjs:262:32&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    at &lt;span class=&quot;token builtin class-name&quot;&gt;eval&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;webpack-internal:///./main.debug.tsx:9:54&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    at Module&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/main.debug.tsx &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http://localhost:8080/app.bundle.js:1126:1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    at __webpack_require__ &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http://localhost:8080/app.bundle.js:724:30&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    at fn &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http://localhost:8080/app.bundle.js:101:20&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    at Object.0 &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http://localhost:8080/app.bundle.js:1139:18&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    at __webpack_require__ &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http://localhost:8080/app.bundle.js:724:30&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately the code is minified and the sourcemaps didn&#39;t seem to work which explains the single letter function names. Nonetheless the error message gives us a few clues. It looks like there is a function called &lt;code&gt;createElement&lt;/code&gt; somwhere that complained about receiving an invalid argument.&lt;/p&gt;
&lt;p&gt;Knowing a thing or two about virtual dom libraries &lt;em&gt;(hint I work on &lt;a href=&quot;https://github.com/developit/preact/&quot;&gt;Preact&lt;/a&gt;)&lt;/em&gt; this is very likely about the JSX constructor function. It&#39;s the one responsible for turning JSX into virtual-dom nodes (short: &lt;code&gt;vnode&lt;/code&gt;) that represent a sort-of template. Most projects transpile it via &lt;code&gt;babel&lt;/code&gt; or the &lt;code&gt;TypeScript&lt;/code&gt; compiler to plain function calls that js engines can understand.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// with props&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;foo&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// with children&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;foo&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These in turn convert the arguments to a &lt;code&gt;vnode&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; vnode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vnode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Logs:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// {&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//   type: &#39;span&#39;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//   props: { class: &#39;foo&#39; }&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//   text: null,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// }&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note: Nearly all virtual-dom based frameworks work with a similar shaped object under the hood. Some rename properties like &lt;code&gt;nodeName&lt;/code&gt; instead of &lt;code&gt;type&lt;/code&gt; but that&#39;s mostly it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So it&#39;s this function is called with something it doesn&#39;t like. Let&#39;s look at the code that triggers it:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// foo.js&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// app.js&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Foo &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&#39;root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing out of the ordinary and it looks like a typical setup for any modern web app. The browser&#39;s debugger was pointing at the highlighted line where the render function is called. It seems innocent, but something must be up with it. Since the render function strangely didn&#39;t turn up as part of the stacktrace we&#39;ll have trouble finding the right place to set a breakpoint. Following imports is usually not much fun when you can&#39;t &lt;code&gt;cmd/ctrl+click&lt;/code&gt; a variable like you can in vscode or similar IDEs.&lt;/p&gt;
&lt;p&gt;In these situations there is one neat little trick we can do without having to dive into the code of the library in use (that&#39;s Preact in this example).&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Foo &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Wrap it and log first argument&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;render2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;arg1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// call our wrapped function&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;render2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&#39;root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And sure enough we receive something suspicious:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;span&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token property&quot;&gt;&quot;props&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token property&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token null keyword&quot;&gt;null&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;props&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token property&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token null keyword&quot;&gt;null&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Well that&#39;s odd, it seems like the &lt;code&gt;type&lt;/code&gt;-property isn&#39;t just the string &lt;code&gt;span&lt;/code&gt; but holds another &lt;code&gt;object&lt;/code&gt; that looks like another &lt;code&gt;vnode&lt;/code&gt;. This nesting is probably invalid, so let&#39;s look again at our code and search for any hints. To make this easier for myself I disabled sourcemaps in the browser&#39;s devtools.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// foo.js&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; createElement &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// app.js&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Foo &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Foo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;root&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wait a second, two calls to &lt;code&gt;createElement&lt;/code&gt;? Do we double encode our JSX element? Yup, we do! We already apply our JSX constructor in &lt;code&gt;foo.js&lt;/code&gt;, so we don&#39;t need to pass it through &lt;code&gt;createElement&lt;/code&gt; again. The fix is easy:.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// replace this...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;root&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// ...with this&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;App&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&#39;root&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So what did we just do there? How did we go from an error to finding the cause?&lt;/p&gt;
&lt;h2&gt;1. Identification 💥&lt;/h2&gt;
&lt;p&gt;The most important part of any debugging story is finding a way to get to an error or into an incorrect state. Only after one has identified the error is one able to fix it. In our case it was quite easy as just firing up
the browser lead to a nice stack trace and we didn&#39;t have to go through any
additional steps to reproduce it. We&#39;re not always that lucky.&lt;/p&gt;
&lt;h2&gt;2. Isolation🔧&lt;/h2&gt;
&lt;p&gt;Once the issue has been identified we need a way to reduce the amount of code we neeed to step through. The less code that causes the error, the better. If you have a stack trace you can often walk it backwards and ignore the rest of your code. For frontend applications you can usually narrow it down to a few or even a single component.&lt;/p&gt;
&lt;h2&gt;3. The Fix ✅&lt;/h2&gt;
&lt;p&gt;At this point you should have found the error and are able to reproduce it in isolation. Now comes the hard part of finding a fix for it. This is highly specific to the code you&#39;re working on so it&#39;s hard to make some general rules here. What I find most useful is trying to understand the state or context of the currently executing function.&lt;/p&gt;
&lt;p&gt;When there is an issue in Preact we need to be very aware of the shape the current vnode tree is in. Knowing that is usually half the work to finding a proper fix.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We fixed it! With some ciritical thinking we were quickly able to pin-point the issue to a &lt;code&gt;vnode&lt;/code&gt; being encoded twice. That&#39;s one more happy Preact user 🎉 Over the years I&#39;ve taken the 3 steps to debugging by heart and they are still the same principles I apply when working with unfamiliar code bases🍀&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>When should I use preact compat?</title>
    <link href="https://marvinh.dev/blog/preact-vs-compat/"/>
    <updated>2019-03-23T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/preact-vs-compat/</id>
    <content type="html">&lt;p&gt;What&#39;s the difference between &lt;code&gt;preact&lt;/code&gt; and &lt;code&gt;preact/compat&lt;/code&gt;? That&#39;s a great and one of the more popular questions if you check out the &lt;code&gt;preact&lt;/code&gt; tag on StackOverflow. Preact advertises itself as the thinnest possible virtual-dom 3kB abstraction over the real DOM with a react-like API 🚀. This sentence is quite a mouthful, but the key is the last part: &amp;quot;&lt;em&gt;react-like&lt;/em&gt; API&amp;quot;.&lt;/p&gt;
&lt;p&gt;While the high-level concepts are very similar in any virtual-dom based library, we all implemented them differently. The beauty about this is that as a user you likely won&#39;t notice it and can reuse your existing knowledge for building modern Single-Page-Applications (=&lt;em&gt;SPA&lt;/em&gt;). This typically includes reyling on several third-party libraries that are available on &lt;code&gt;npm&lt;/code&gt; and oh boy are there many of them!&lt;/p&gt;
&lt;p&gt;So we looked at ways on how we can leverage existing libraries. We didn&#39;t want to put the workload on the community by asking them to rewrite everything for Preact and instead decided to provide a compatibility layer that sits above Preact. That&#39;s why it&#39;s called &lt;code&gt;preact/compat&lt;/code&gt;. So what does the compatibility layer contain that makes libraries written for React seamlessly work with Preact?&lt;/p&gt;
&lt;h2&gt;The curious case of the Children API&lt;/h2&gt;
&lt;p&gt;If you ever wrote components that resemble a generic list like an AutoSuggest-component you&#39;ll likely want to wrap each list item with something for styling or other purposes. To do this you need a way to iterate over the &lt;code&gt;children&lt;/code&gt; and wrap each child with an &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;-tag for example. The thing to watch out for is that &lt;code&gt;props.children&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; guaranteed to be an &lt;code&gt;array&lt;/code&gt;, so you can&#39;t just call &lt;code&gt;props.children.map(fn)&lt;/code&gt;. When is that the case?&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// `props.children` will be an object&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bar&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// `props.children` will be an array&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bar&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;  &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bar&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To solve this React provides a wrapper that can map over &lt;code&gt;props.children&lt;/code&gt; in a similar way like you would for an &lt;code&gt;array&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Children &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AutoSuggest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Children&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;someId&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;fancy-list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;						&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;child&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;					&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;		&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Besides the &lt;code&gt;map()&lt;/code&gt; function the &lt;code&gt;Children&lt;/code&gt;-API supports a few more methods:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Children.forEach(children, fn)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply &lt;code&gt;fn&lt;/code&gt; on each child, returns &lt;code&gt;void&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Children.map(children, fn)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply &lt;code&gt;fn&lt;/code&gt; on each child and return an array of children&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Children.count(children)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns the number of children&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Children.toArray(children)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Converts children to an array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Children.only(children)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Throws when there is more or less than &lt;strong&gt;1&lt;/strong&gt; child&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If you squint slightly all these methods are very similar to those found on a standard array. They even share the same name in fact. Historically children have always been an array in Preact. This means that you could always savely call &lt;code&gt;props.children.map&lt;/code&gt; etc. For Preact X we unfortunately had to change this to support parsing ambiguities with &lt;code&gt;Fragements&lt;/code&gt;. Nonetheless we&#39;ve kept the ease of use of arrays and provide our own method to convert &lt;code&gt;props.children&lt;/code&gt; to an array:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; toChildArray &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AutoSuggest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toChildArray&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;child&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;					&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;someId&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;fancy-list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;						&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;child&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;					&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;				&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;		&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The advantage of this is that our API surface remains small. You don&#39;t have to learn any new ways to count the items in an array because already do know that. So effectively the &lt;code&gt;Children&lt;/code&gt;-API can be replaced with straight array methods in Preact:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;Array Method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Children.forEach(children, fn)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arr.forEach(fn)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Children.map(children, fn)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arr.map(fn)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Children.count(children)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arr.length&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Children.toArray(children)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;toChildArray(children)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Children.only(children)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;needs to be implemented manually&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;An argument can be made that creating an array when there is only a single child present is wasteful, but we happily trade that of for a much simpler API. That single allocation is so tiny, that it&#39;s extremly difficult to measure even with microbenchmarks.&lt;/p&gt;
&lt;h2&gt;Unmounting a root node with style&lt;/h2&gt;
&lt;p&gt;Although it&#39;s quite rare, there are some instances where you need to destroy a root node. A root node referes to the topmost node of a tree. It&#39;s the one you passed into &lt;code&gt;render()&lt;/code&gt; with your component as the first argument.&lt;/p&gt;
&lt;p&gt;Adding another function export in our code base is always expensive for size reasons and we always try to avoid adding any new exports. Instead we can leverage the fact that &lt;code&gt;null&lt;/code&gt; is alwayst treated as an empty value throughout our whole code base. Because of that we can literally just call &lt;code&gt;render()&lt;/code&gt; with &lt;code&gt;null&lt;/code&gt; to destroy a root node:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Hello World&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;root&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Destroy the root&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;root&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even our compatibility layer in &lt;code&gt;preact/compat&lt;/code&gt; for &lt;code&gt;unmountComponentAtNode&lt;/code&gt; does just that:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; render &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Remove a component tree from the DOM,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// including state and event handlers.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unmountComponentAtNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_prevVNode &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; container&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;PureComponent and memo()&lt;/h2&gt;
&lt;p&gt;Both the &lt;code&gt;PureComponent&lt;/code&gt; class and the recently introduced &lt;code&gt;memo()&lt;/code&gt; function are ways to specificy a comparison function to potentially skip out of an update. &lt;code&gt;PureComponent&lt;/code&gt; is for classes and ships with a default implementation for &lt;code&gt;shouldComponentUpdate&lt;/code&gt;. &lt;code&gt;memo()&lt;/code&gt; is for functional components respectively and does the same thing. They are both meant to improve performance when the wrapped components are so expensive to render that they effect the user experience negatively.&lt;/p&gt;
&lt;p&gt;In our experience this is only true for a very tiny percentage of apps built with Preact. Nearly all performance issues are caused by code that unnecessarily calls &lt;code&gt;setState&lt;/code&gt; or &lt;code&gt;forceUpdate&lt;/code&gt;. Of course you can always try to solve it with &lt;code&gt;memo&lt;/code&gt; or &lt;code&gt;PureComponent&lt;/code&gt; but solving the issue at the root is much more beneficial and in my experience easier to reason about. It usually also makes the code easier to read, which is always a plus!&lt;/p&gt;
&lt;h2&gt;What about forwardRef?&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;forwardRef&lt;/code&gt; is an interesting one, because it&#39;s a solution to a problem that we framework authors hope to avoid in the first place. The problem is that the &lt;code&gt;ref&lt;/code&gt; and &lt;code&gt;key&lt;/code&gt; properties are filtered out of the &lt;code&gt;props&lt;/code&gt; object:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ref&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// logs: `undefined`, `undefined`&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;whatever&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;bob&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dom&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though we passed both properties explicitely to &lt;code&gt;Foo&lt;/code&gt;, they won&#39;t be available anymore on &lt;code&gt;props&lt;/code&gt;. This is caused by &lt;code&gt;createElement&lt;/code&gt; filtering out those two properties. &lt;code&gt;forwardRef&lt;/code&gt; was brought into existance to pass &lt;code&gt;ref&lt;/code&gt; back into a component, because it turns out that &lt;em&gt;forwarding&lt;/em&gt; ref properties is very useful for libraries.&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; forwardRef &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact/compat&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Some high-order component&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withLog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;Wrapped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;forwardRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bar&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;          &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Wrapped&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token spread&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;ref&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bar&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What if we just didn&#39;t delete &lt;code&gt;ref&lt;/code&gt; from &lt;code&gt;props&lt;/code&gt;? That would make &lt;code&gt;forwardRef&lt;/code&gt; completely unnecessary and there even is an open &lt;a href=&quot;https://github.com/reactjs/rfcs/pull/107&quot;&gt;RFC&lt;/a&gt; for that over at the react repo. We had this at one point in Preact X before going alpha and filtered it out to stay compatible with React. Naturally we&#39;re very excited about making the API surface slimmer and reusing existing solutions ✅.&lt;/p&gt;
&lt;p&gt;Personally I think we should go back and only remove &lt;code&gt;ref&lt;/code&gt; from props in &lt;code&gt;preact/compat&lt;/code&gt;. This should be an easy change to make and would be a good first PR 🎉 With that change in place could simply be rewritten to be a standard functional component, no &lt;code&gt;forwardRef&lt;/code&gt; is needed:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Some high-order component&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withLog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;Wrapped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bar&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Wrapped&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token spread&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ref&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;      &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bar&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Patching in React-specific properties&lt;/h2&gt;
&lt;p&gt;In my last blog post we talked about using &lt;a href=&quot;https://marvinh.dev/blog/preact-options/&quot;&gt;Preact&#39;s option hooks&lt;/a&gt; to modify our data structures. We use it to add properties that we don&#39;t use but many third-party libraries check for. These include &lt;code&gt;$$typeof&lt;/code&gt; to check if an virtual dom node was created by &lt;code&gt;createElement&lt;/code&gt;, &lt;code&gt;Component.isReactComponent&lt;/code&gt; which is just a simple &lt;code&gt;boolean&lt;/code&gt; flag and a few more. These just have to be there but aren&#39;t really used in Preact.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Phew I wrote more than I intended to but I hope that this quick overview gives you an idea why &lt;code&gt;preact/compat&lt;/code&gt; exists and why certain things are not found in the core library. If you feel like something is missing, feel free to get in touch or file an issue on our repo in &lt;a href=&quot;https://github.com/developit/preact&quot;&gt;github&lt;/a&gt; 👍&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Preact&#39;s best kept secret</title>
    <link href="https://marvinh.dev/blog/preact-options/"/>
    <updated>2019-03-17T00:00:00Z</updated>
    <id>https://marvinh.dev/blog/preact-options/</id>
    <content type="html">&lt;p&gt;I though I&#39;d start the blog with something special that not many Preact users may know about. It&#39;s one of the least known features and also one of the most powerful ways to extend Preact: Option-Hooks. They allow anyone to plug into our reconciler and extend Preact without needing to make any modifications in our core. This power is what enables us to ship various addons like &lt;code&gt;preact/compat&lt;/code&gt; and &lt;code&gt;preact/hooks&lt;/code&gt; to name a few. They&#39;ve been in Preact since the very early days reaching back to 2015 ✅.&lt;/p&gt;
&lt;p&gt;⚠ &lt;em&gt;Note: Internally they&#39;ve always been referred to as hooks in our code base. They are not to be confused with the recent hooks feature that was introduced by the react team.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;At the time of this writing we expose 9 hooks for the various reconciliation stages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;options.vnode&lt;/code&gt;: Allows to mutate a &lt;code&gt;vnode&lt;/code&gt; just before passing it to the reconciler&lt;/li&gt;
&lt;li&gt;&lt;code&gt;options.commit&lt;/code&gt;: Called when a whole &lt;code&gt;vnode&lt;/code&gt;-tree is mounted or updated&lt;/li&gt;
&lt;li&gt;&lt;code&gt;options.unmount&lt;/code&gt;: Called when a &lt;code&gt;vnode&lt;/code&gt; will be removed from the DOM.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;options.diff&lt;/code&gt;: Called right before a &lt;code&gt;vnode&lt;/code&gt; is compared against the previous one&lt;/li&gt;
&lt;li&gt;&lt;code&gt;options.render&lt;/code&gt;: Called right before rendering a component&lt;/li&gt;
&lt;li&gt;&lt;code&gt;options.diffed&lt;/code&gt;: Called after the comparison between 2 &lt;code&gt;vnodes&lt;/code&gt; finished&lt;/li&gt;
&lt;li&gt;&lt;code&gt;options.event&lt;/code&gt;: Called when an event handler will be dispatched&lt;/li&gt;
&lt;li&gt;&lt;code&gt;options.requestAnimationFrame&lt;/code&gt;: Called when a layout effect will be scheduled&lt;/li&gt;
&lt;li&gt;&lt;code&gt;options.debounceRendering&lt;/code&gt;: Called when an update is scheduled&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Note: &lt;code&gt;vnode&lt;/code&gt; (= virtual DOM node) is the data structure at the heart of Preact and is used thorough the diff process to compare elements to each other.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You may rightfully ask why we didn&#39;t choose more descriptive name for these. The reason behind is our strong focus on bundle size. By re-using words that can already be found elsewhere in our code we can play in the hands of the gzip compression algorithm ✌️. It&#39;s optimized to optimally compress repeating patterns and we have quite a few of them.&lt;/p&gt;
&lt;h2&gt;Extending Preact via addons&lt;/h2&gt;
&lt;p&gt;A great example of where they&#39;re used is in the recently added &lt;code&gt;preact/hooks&lt;/code&gt; addon. Whenever a hook is invoked it will be attached to the current component so that it can be called again once the component rerenders. But because hook functions don&#39;t receive any reference to the component through any function arguments, we need to keep track of the current component through other means. For this we can leverage &lt;code&gt;options.render&lt;/code&gt; to just store the reference in a variable and &lt;code&gt;options.diffed&lt;/code&gt; after it&#39;s done rendering to flush any pending effects.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; options &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Reference to the component that&#39;s currently rendering&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; currentComponent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Index into array of hooks on a component instance&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; currentIndex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Keep track of the current component and index of the hook. Each hook will&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// increment the index automatically.&lt;/span&gt;&lt;br /&gt;options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;vnode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	currentComponent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vnode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_component&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	currentIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Schedule any pending effects after the component is done rendering&lt;/span&gt;&lt;br /&gt;options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;diffed&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;vnode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hooks &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vnode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_component&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_hooks&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	hooks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;_pendingEffects&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;effect&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;invokeEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;effect&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;preact/compat&lt;/code&gt; employs a similar trick: To make third-party libraries belief that we are React we need to patch our internal &lt;code&gt;vnode&lt;/code&gt; structure with React-specific properties. Many 3rd-party libraries explicitely check for them and will bail out if they are missing. So we need to apply normalizations as early as possible before any 3rd-party code is run. In our case we chose to do this right when a &lt;code&gt;vnode&lt;/code&gt; is created in &lt;code&gt;createElement&lt;/code&gt; (or &lt;code&gt;h&lt;/code&gt;). Internally it relies on &lt;code&gt;options.vnode&lt;/code&gt; to do the magic:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Pseudo implementation for jsx constructor&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;children&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; vnode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createVNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; props&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; children&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vnode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;vnode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vnode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; vnode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// In `preact/compat`:&lt;/span&gt;&lt;br /&gt;options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;vnode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;vnode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Add react-specifc $$typeof property&lt;/span&gt;&lt;br /&gt;	vnode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;$$&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;REACT_ELEMENT_TYPE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Besides allowing addons for greater ecosystem compatibiliy the options hooks open up a whole world of experiments and prototypes. We have a plethora of them spread out over gists, codesandboxes and fiddles that prototype various ideas. These quick prototypes are a lot easier to share and allow us to evaluate if it&#39;s any good before spending the time integrating it natively into core.&lt;/p&gt;
&lt;p&gt;One of our users &lt;a href=&quot;https://twitter.com/mindplaydk&quot;&gt;Rasmus Schultz&lt;/a&gt; for example &lt;a href=&quot;https://codesandbox.io/s/z6x4vkx2rp&quot;&gt;shared&lt;/a&gt; a different form of components an alternative to hooks.&lt;/p&gt;
&lt;h2&gt;Hooks re-imagined&lt;/h2&gt;
&lt;p&gt;They&#39;re largerly inspired by the way components are handled in &lt;a href=&quot;https://github.com/localvoid/ivi/&quot;&gt;ivi&lt;/a&gt; which leverages closures to circumvent the need to keep track of the current component. Previously we established that we only need to do that because a hook doesn&#39;t receive the component reference in any function argument. This way of declaring components does exactly that:&lt;/p&gt;
&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; Counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;component&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		value &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// `invalidate` is conceptually the same as `setState`&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;invalidate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instance&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;instance&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;You clicked &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;value&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; times&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Return a function (=render method)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;			&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&lt;br /&gt;		&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just by wrapping our component implementation with a function that initializes it. Instead of returning the virtual DOM directly, it returns another function as the render method. The outer function is used to initialize all hooks or event handlers and basically acts as a constructor for the render method. This has the benefit of not having to re-create functions on each &lt;code&gt;render&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The downside is that &lt;code&gt;props&lt;/code&gt; can only be accessed via a reference to the instance. They are not &lt;code&gt;value&lt;/code&gt; bound anymore which can lead to tearing when mixed with asynchronicity. Nonetheless this is a great alternative to hooks which just chooses a different set of tradeoffs. And we always love and encourage such experiments. Before you know it these may turn out to be something really cool ⚡.&lt;/p&gt;
&lt;p&gt;Remember even Preact X just started out as a random experiment and before we knew it, turned into the next major version for Preact. For that reason our option hooks our little secret allowing us to quickly iterate and prototype new ideas 🙌.&lt;/p&gt;
</content>
  </entry>
</feed>